Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions src/components/forms/__tests__/event-form.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ describe("EventForm", () => {
moderator: null,
sponsors: [],
tags: [],
extra_questions: []
extra_questions: [],
// The Materials panel is expanded by renderEventForm, so uicore's Table now
Comment thread
caseylocker marked this conversation as resolved.
Outdated
// actually mounts and maps over this.
materials: []
},
errors: {},
onSubmit: jest.fn(),
Expand All @@ -82,8 +85,18 @@ describe("EventForm", () => {
onClone: jest.fn()
};

const renderEventForm = (overrides = {}) =>
render(<EventForm {...baseProps} {...overrides} />);
// The reopen section lives inside the Materials panel, and uicore's Panel mounts
Comment thread
caseylocker marked this conversation as resolved.
Outdated
// its children only while expanded, so every assertion here needs it open first.
// queryByText, not getByText: the panel itself is gated on a saved presentation,
// and the tests that assert the control is absent pass entities that suppress it.
const renderEventForm = (overrides = {}) => {
const result = render(<EventForm {...baseProps} {...overrides} />);
const materialsHeading = screen.queryByText(/^edit_event\.materials/, {
selector: ".panel-title"
});
if (materialsHeading) fireEvent.click(materialsHeading);
return result;
};

// Built on baseProps.entity (not the bare object from the task brief) because
// several unrelated, pre-existing render paths (TagInput, isEventType,
Expand Down Expand Up @@ -117,6 +130,14 @@ describe("EventForm", () => {
).toBeInTheDocument();
});

it("leaves the Materials panel title plain when there is no grant", () => {
renderEventForm({ entity: baseEntity });

expect(
screen.getByText("edit_event.materials", { selector: ".panel-title" })
).toBeInTheDocument();
});

it("does not offer the reopen control on a new presentation", () => {
renderEventForm({ entity: { ...baseEntity, id: 0 } });

Expand Down Expand Up @@ -183,6 +204,11 @@ describe("EventForm", () => {
expect(
screen.queryByText(/edit_event.reopened_until/)
).not.toBeInTheDocument();
// The panel title shares the section's gate for exactly this case: keyed on the
// grant alone it would announce a deadline the server no longer honours.
expect(
screen.getByText("edit_event.materials", { selector: ".panel-title" })
).toBeInTheDocument();
});

it("disables the reopen button when no valid hours value is selected", async () => {
Expand Down Expand Up @@ -385,6 +411,16 @@ describe("EventForm", () => {
).not.toBeInTheDocument();
});

it("announces the deadline on the collapsed Materials panel title", () => {
renderEventForm({ entity: grantedEntity });

expect(
screen.getByText("edit_event.materials_reopened", {
selector: ".panel-title"
})
).toBeInTheDocument();
});
Comment thread
caseylocker marked this conversation as resolved.

it("still shows the reopened state when the payload was not expanded", () => {
renderEventForm({ entity: grantedUnexpandedEntity });

Expand Down
259 changes: 138 additions & 121 deletions src/components/forms/event-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,20 @@ class EventForm extends React.Component {
return moment().unix() > plan.submission_end_date;
}

// The panel title and the section body MUST share one gate. Keying the title on
Comment thread
caseylocker marked this conversation as resolved.
Outdated
// isSubmissionReopened() alone would announce a deadline in exactly the case the
// comment on isReopenApplicable describes: a live grant whose plan window was
// since extended, which the server no longer treats as operative.
isReopenSectionVisible() {
const { entity } = this.state;
return (
this.isPresentation() &&
!this.isNew() &&
entity.selection_plan_id > 0 &&
this.isReopenApplicable()
);
}

isSubmissionReopened() {
const deadline = this.getReopenDeadline();
return !!deadline?.isAfter(moment());
Expand Down Expand Up @@ -1259,126 +1273,6 @@ class EventForm extends React.Component {
</p>
</div>
)}
{this.isPresentation() &&
!this.isNew() &&
entity.selection_plan_id > 0 &&
this.isReopenApplicable() && (
<div className="row form-group">
<div className="col-md-12">
<label>
{T.translate("edit_event.reopen_submission_section")}
</label>
{!this.isSubmissionReopened() && (
<div
style={{ display: "flex", alignItems: "center", gap: 10 }}
>
<label htmlFor="reopen_hours">
{T.translate("edit_event.reopen_duration")}
</label>
<select
id="reopen_hours"
className="form-control"
style={{ width: "auto" }}
value={reopenHours}
onChange={(ev) =>
this.setState({ reopenHours: ev.target.value })
}
>
<option value={DEFAULT_REOPEN_HOURS}>
{T.translate("edit_event.reopen_duration_24")}
</option>
<option value={REOPEN_PRESET_HOURS_48}>
{T.translate("edit_event.reopen_duration_48")}
</option>
<option value={REOPEN_PRESET_HOURS_72}>
{T.translate("edit_event.reopen_duration_72")}
</option>
<option value="custom">
{T.translate("edit_event.reopen_duration_custom")}
</option>
</select>
{reopenHours === "custom" && (
<>
<label htmlFor="reopen_custom_hours">
{maxReopenHours
? T.translate(
"edit_event.reopen_custom_hours_capped",
{ max: maxReopenHours }
)
: T.translate("edit_event.reopen_custom_hours")}
</label>
<input
id="reopen_custom_hours"
type="number"
min="1"
max={maxReopenHours || undefined}
className="form-control"
style={{ width: 120 }}
value={reopenCustomHours}
onChange={(ev) =>
this.setState({
reopenCustomHours: ev.target.value
})
}
/>
</>
)}
<button
type="button"
className="btn btn-primary"
disabled={!this.getSelectedReopenHours()}
onClick={this.handleReopenSubmission}
>
{T.translate("edit_event.reopen_submission")}
</button>
</div>
)}
{this.isSubmissionReopened() && (
<div
style={{
display: "flex",
alignItems: "center",
gap: 10,
flexWrap: "wrap"
}}
>
<span>
{T.translate("edit_event.reopened_until", {
deadline: this.getReopenDeadline().format(
REOPEN_DEADLINE_FORMAT
)
})}
</span>
{entity.submission_reopened_by && (
<span>
{T.translate("edit_event.reopened_by", {
admin: `${entity.submission_reopened_by.first_name} ${entity.submission_reopened_by.last_name}`
})}
</span>
)}
<button
type="button"
className="btn btn-danger"
onClick={this.handleCloseSubmission}
>
{T.translate("edit_event.close_submission")}
</button>
{window.CFP_APP_BASE_URL && (
<span>
<label>
{T.translate("edit_event.reopen_deep_link_label")}
</label>
&nbsp;
<CopyClipboard text={speakerDeepLink} />
&nbsp;
{speakerDeepLink}
</span>
)}
</div>
)}
</div>
</div>
)}
<div className="row form-group">
<div className="col-md-8">
<label> {T.translate("edit_event.submitter")} </label> &nbsp;
Expand Down Expand Up @@ -2073,8 +1967,17 @@ class EventForm extends React.Component {
</Panel>
{entity.id != 0 && this.isEventType(EVENT_TYPE_PRESENTATION) && (
<Panel
id="materials"
show={showSection === "materials"}
title={T.translate("edit_event.materials")}
title={
this.isReopenSectionVisible() && this.isSubmissionReopened()
Comment thread
caseylocker marked this conversation as resolved.
Outdated
? T.translate("edit_event.materials_reopened", {
deadline: this.getReopenDeadline().format(
REOPEN_DEADLINE_FORMAT
)
})
: T.translate("edit_event.materials")
}
handleClick={this.toggleSection.bind(this, "materials")}
>
<button
Expand All @@ -2088,6 +1991,120 @@ class EventForm extends React.Component {
data={entity.materials}
columns={material_columns}
/>
{this.isReopenSectionVisible() && (
<div className="row form-group">
<div className="col-md-12">
{!this.isSubmissionReopened() && (
<div
style={{ display: "flex", alignItems: "center", gap: 10 }}
>
<label htmlFor="reopen_hours">
{T.translate("edit_event.reopen_duration")}
</label>
<select
id="reopen_hours"
className="form-control"
style={{ width: "auto" }}
value={reopenHours}
onChange={(ev) =>
this.setState({ reopenHours: ev.target.value })
}
>
<option value={DEFAULT_REOPEN_HOURS}>
{T.translate("edit_event.reopen_duration_24")}
</option>
<option value={REOPEN_PRESET_HOURS_48}>
{T.translate("edit_event.reopen_duration_48")}
</option>
<option value={REOPEN_PRESET_HOURS_72}>
{T.translate("edit_event.reopen_duration_72")}
</option>
<option value="custom">
{T.translate("edit_event.reopen_duration_custom")}
</option>
</select>
{reopenHours === "custom" && (
<>
<label htmlFor="reopen_custom_hours">
{maxReopenHours
? T.translate(
"edit_event.reopen_custom_hours_capped",
{ max: maxReopenHours }
)
: T.translate("edit_event.reopen_custom_hours")}
</label>
<input
id="reopen_custom_hours"
type="number"
min="1"
max={maxReopenHours || undefined}
className="form-control"
style={{ width: 120 }}
value={reopenCustomHours}
onChange={(ev) =>
this.setState({
reopenCustomHours: ev.target.value
})
}
/>
</>
)}
<button
type="button"
className="btn btn-primary"
disabled={!this.getSelectedReopenHours()}
onClick={this.handleReopenSubmission}
>
{T.translate("edit_event.reopen_submission")}
</button>
</div>
)}
{this.isSubmissionReopened() && (
<div
style={{
display: "flex",
alignItems: "center",
gap: 10,
flexWrap: "wrap"
}}
>
<span>
{T.translate("edit_event.reopened_until", {
deadline: this.getReopenDeadline().format(
REOPEN_DEADLINE_FORMAT
)
})}
</span>
{entity.submission_reopened_by && (
<span>
{T.translate("edit_event.reopened_by", {
admin: `${entity.submission_reopened_by.first_name} ${entity.submission_reopened_by.last_name}`
})}
</span>
)}
<button
type="button"
className="btn btn-danger"
onClick={this.handleCloseSubmission}
>
{T.translate("edit_event.close_submission")}
</button>
{window.CFP_APP_BASE_URL && (
<span>
<label>
{T.translate("edit_event.reopen_deep_link_label")}
</label>
&nbsp;
<CopyClipboard text={speakerDeepLink} />
&nbsp;
{speakerDeepLink}
</span>
)}
</div>
)}
</div>
</div>
)}
</Panel>
)}

Expand Down
2 changes: 1 addition & 1 deletion src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,7 @@
"event_cloned": "Activity cloned successfully.",
"proximity_alert": "Speaker/s have sessions 90min before or after:",
"materials": "Materials",
"materials_reopened": "Materials — submission reopened until {deadline}",
"add_material": "Add Material",
"display_on_site": "Display on site",
"type": "Type",
Expand Down Expand Up @@ -714,7 +715,6 @@
"save_and_mark_complete": "Save & Mark Complete",
"event_saved_as_draft": "Activity saved as draft successfully.",
"reopen_submission": "Reopen submission",
"reopen_submission_section": "CFP submission window",
"reopen_duration": "Reopen for",
"reopen_duration_24": "24 hours",
"reopen_duration_48": "48 hours",
Expand Down
Loading