Modal
An overlay that appears in front of all other content, and requires a user to take an action before continuing.
Props
heading
string
The heading text displayed at the top of the modal.
closable
boolean
Show close icon and allow clicking the background to close the modal.
Defaults to
false.
open
boolean
Controls if modal is visible or not.
Defaults to
false.
transition
fast | slow | none
Sets the animation transition when opening/closing. 'fast' or 'slow' for animated, 'none' for instant.
Defaults to
none.
calloutVariant
""
Define the context and colour of the callout modal. It is required when type is set to callout.
maxWidth
string
Set the max allowed width of the modal.
Defaults to
60ch.
testId
string
Sets a data-testid attribute for automated testing.
Defaults to
modal.
width
string
Use maxwidth instead.
Events
onClose
(event: Event) => void
_close
CustomEvent
Slots
heading
Named slot for content
content
Named slot for content
actions
Named slot for content
const [open, setOpen] = useState(false);
const [type, setType] = useState<string>();
const [name, setName] = useState<string>();
const [description, setDescription] = useState<string>();<GoabxButton type="tertiary" leadingIcon="add" onClick={() => setOpen(true)}>
Add another item
</GoabxButton>
<GoabxModal
heading="Add a new item"
open={open}
actions={
<GoabButtonGroup alignment="end">
<GoabxButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
Cancel
</GoabxButton>
<GoabxButton type="primary" size="compact" onClick={() => setOpen(false)}>
Save new item
</GoabxButton>
</GoabButtonGroup>
}
>
<p>Fill in the information to create a new item</p>
<GoabxFormItem label="Type" mt="l">
<GoabxDropdown onChange={(e) => setType(e.value)} value={type}>
<GoabxDropdownItem value="1" label="Option 1" />
<GoabxDropdownItem value="2" label="Option 2" />
</GoabxDropdown>
</GoabxFormItem>
<GoabxFormItem label="Name" mt="l">
<GoabxInput
onChange={(e) => setName(e.value)}
value={name}
name="name"
width="100%"
/>
</GoabxFormItem>
<GoabxFormItem label="Description" mt="l">
<GoabxTextArea
name="description"
rows={3}
width="100%"
onChange={(e) => setDescription(e.value)}
value={description}
/>
</GoabxFormItem>
</GoabxModal>open = false;
type: string | undefined = "";
name = "";
description = "";
toggleModal() {
this.open = !this.open;
}
updateType(event: any) {
this.type = event.value;
}
updateName(event: any) {
this.name = event.value;
}
updateDescription(event: any) {
this.description = event.value;
}<goabx-button type="tertiary" leadingIcon="add" (onClick)="toggleModal()">Add another item</goabx-button>
<goabx-modal [open]="open" (onClose)="toggleModal()" heading="Add a new item" [actions]="actions">
<p>Fill in the information to create a new item</p>
<goabx-form-item label="Type" mt="l">
<goabx-dropdown (onChange)="updateType($event)" [value]="type">
<goabx-dropdown-item value="1" label="Option 1"></goabx-dropdown-item>
<goabx-dropdown-item value="2" label="Option 2"></goabx-dropdown-item>
</goabx-dropdown>
</goabx-form-item>
<goabx-form-item label="Name" mt="l">
<goabx-input name="name" width="100%" (onChange)="updateName($event)" [value]="name"></goabx-input>
</goabx-form-item>
<goabx-form-item label="Description" mt="l">
<goabx-textarea name="description" width="100%" [rows]="3" (onChange)="updateDescription($event)" [value]="description"></goabx-textarea>
</goabx-form-item>
<ng-template #actions>
<goab-button-group alignment="end">
<goabx-button type="tertiary" size="compact" (onClick)="toggleModal()">Cancel</goabx-button>
<goabx-button type="primary" size="compact" (onClick)="toggleModal()">Save new item</goabx-button>
</goab-button-group>
</ng-template>
</goabx-modal>const modal = document.getElementById("add-item-modal");
const openBtn = document.getElementById("open-modal-btn");
const cancelBtn = document.getElementById("cancel-btn");
const saveBtn = document.getElementById("save-btn");
openBtn.addEventListener("_click", () => {
modal.setAttribute("open", "true");
});
modal.addEventListener("_close", () => {
modal.removeAttribute("open");
});
cancelBtn.addEventListener("_click", () => {
modal.removeAttribute("open");
});
saveBtn.addEventListener("_click", () => {
modal.removeAttribute("open");
});<goa-button version="2" id="open-modal-btn" type="tertiary" leadingicon="add">Add another item</goa-button>
<goa-modal version="2" id="add-item-modal" heading="Add a new item">
<p>Fill in the information to create a new item</p>
<goa-form-item version="2" label="Type" mt="l">
<goa-dropdown version="2" id="type-dropdown">
<goa-dropdown-item value="1" label="Option 1"></goa-dropdown-item>
<goa-dropdown-item value="2" label="Option 2"></goa-dropdown-item>
</goa-dropdown>
</goa-form-item>
<goa-form-item version="2" label="Name" mt="l">
<goa-input version="2" name="name" width="100%" id="name-input"></goa-input>
</goa-form-item>
<goa-form-item version="2" label="Description" mt="l">
<goa-textarea version="2" name="description" width="100%" rows="3" id="description-textarea"></goa-textarea>
</goa-form-item>
<div slot="actions">
<goa-button-group alignment="end">
<goa-button version="2" id="cancel-btn" type="tertiary" size="compact">Cancel</goa-button>
<goa-button version="2" id="save-btn" type="primary" size="compact">Save new item</goa-button>
</goa-button-group>
</div>
</goa-modal>Confirm a change
const [open, setOpen] = useState(false);
const [effectiveDate, setEffectiveDate] = useState<Date | undefined>(new Date());
const onChangeEffectiveDate = (detail: GoabDatePickerOnChangeDetail) => {
setEffectiveDate(detail.value as Date);
};<GoabxButton onClick={() => setOpen(true)}>Save and continue</GoabxButton>
<GoabxModal
heading="Address has changed"
open={open}
onClose={() => setOpen(false)}
actions={
<GoabButtonGroup alignment="end">
<GoabxButton type="secondary" size="compact" onClick={() => setOpen(false)}>
Undo address change
</GoabxButton>
<GoabxButton type="primary" size="compact" onClick={() => setOpen(false)}>
Confirm
</GoabxButton>
</GoabButtonGroup>
}>
<GoabContainer type="non-interactive" accent="filled" padding="compact" width="full">
<GoabText as="h4" mt="none" mb="s">Before</GoabText>
<GoabText mt="none">123456 78 Ave NW, Edmonton, Alberta</GoabText>
<GoabText as="h4" mt="none" mb="s">After</GoabText>
<GoabText mt="none" mb="none">881 12 Ave NW, Edmonton, Alberta</GoabText>
</GoabContainer>
<GoabxFormItem label="Effective date" mt="l">
<GoabxDatePicker
onChange={onChangeEffectiveDate}
name="effectiveDate"
value={effectiveDate}
/>
</GoabxFormItem>
</GoabxModal>open = false;
effectiveDate = new Date();
toggleModal(): void {
this.open = !this.open;
}
onChangeEffectiveDate(event: GoabDatePickerOnChangeDetail): void {
this.effectiveDate = event.value as Date;
}<goabx-button (onClick)="toggleModal()">Save and continue</goabx-button>
<goabx-modal [open]="open" (onClose)="toggleModal()" heading="Address has changed" [actions]="actions">
<goab-container type="non-interactive" accent="filled" padding="compact" width="full">
<goab-text as="h4" mt="none" mb="s">Before</goab-text>
<goab-text mt="none">123456 78 Ave NW, Edmonton, Alberta</goab-text>
<goab-text as="h4" mt="none" mb="s">After</goab-text>
<goab-text mt="none" mb="none">881 12 Ave NW, Edmonton, Alberta</goab-text>
</goab-container>
<goabx-form-item label="Effective date" mt="l">
<goabx-date-picker (onChange)="onChangeEffectiveDate($event)" name="effectiveDate" [value]="effectiveDate"></goabx-date-picker>
</goabx-form-item>
<ng-template #actions>
<goab-button-group alignment="end">
<goabx-button type="secondary" size="compact" (onClick)="toggleModal()">
Undo address change
</goabx-button>
<goabx-button type="primary" size="compact" (onClick)="toggleModal()">
Confirm
</goabx-button>
</goab-button-group>
</ng-template>
</goabx-modal>const modal = document.getElementById('change-modal');
const openBtn = document.getElementById('open-modal-btn');
const undoBtn = document.getElementById('undo-btn');
const confirmBtn = document.getElementById('confirm-btn');
const datePicker = document.getElementById('effective-date');
let effectiveDate = new Date();
datePicker.setAttribute('value', effectiveDate.toISOString());
openBtn.addEventListener('_click', () => {
modal.setAttribute('open', 'true');
});
undoBtn.addEventListener('_click', () => {
modal.removeAttribute('open');
});
confirmBtn.addEventListener('_click', () => {
modal.removeAttribute('open');
});
modal.addEventListener('_close', () => {
modal.removeAttribute('open');
});
datePicker.addEventListener('_change', (e) => {
effectiveDate = e.detail.value;
});<goa-button version="2" id="open-modal-btn">Save and continue</goa-button>
<goa-modal version="2" id="change-modal" heading="Address has changed">
<goa-container type="non-interactive" accent="filled" padding="compact" width="full">
<goa-text as="h4" mt="none" mb="s">Before</goa-text>
<goa-text mt="none">123456 78 Ave NW, Edmonton, Alberta</goa-text>
<goa-text as="h4" mt="none" mb="s">After</goa-text>
<goa-text mt="none" mb="none">881 12 Ave NW, Edmonton, Alberta</goa-text>
</goa-container>
<goa-form-item version="2" label="Effective date" mt="l">
<goa-date-picker version="2" id="effective-date" name="effectiveDate"></goa-date-picker>
</goa-form-item>
<div slot="actions">
<goa-button-group alignment="end">
<goa-button version="2" type="secondary" size="compact" id="undo-btn">
Undo address change
</goa-button>
<goa-button version="2" type="primary" size="compact" id="confirm-btn">
Confirm
</goa-button>
</goa-button-group>
</div>
</goa-modal>Confirm a destructive action
const [open, setOpen] = useState(false);<GoabxButton
type="tertiary"
leadingIcon="trash"
onClick={() => setOpen(true)}>
Delete record
</GoabxButton>
<GoabxModal
heading="Are you sure you want to delete this record?"
open={open}
onClose={() => setOpen(false)}
actions={
<GoabButtonGroup alignment="end">
<GoabxButton type="tertiary" size="compact" onClick={() => setOpen(false)}>
Cancel
</GoabxButton>
<GoabxButton
type="primary"
variant="destructive"
size="compact"
onClick={() => setOpen(false)}>
Delete record
</GoabxButton>
</GoabButtonGroup>
}>
<p>This action cannot be undone.</p>
</GoabxModal>open = false;
toggleModal(): void {
this.open = !this.open;
}<goabx-button type="tertiary" leadingIcon="trash" (onClick)="toggleModal()">Delete record</goabx-button>
<goabx-modal [open]="open" (onClose)="toggleModal()" heading="Are you sure you want to delete this record?" [actions]="actions">
<p>This action cannot be undone.</p>
<ng-template #actions>
<goab-button-group alignment="end">
<goabx-button type="tertiary" size="compact" (onClick)="toggleModal()">Cancel</goabx-button>
<goabx-button type="primary" variant="destructive" size="compact" (onClick)="toggleModal()">Delete record</goabx-button>
</goab-button-group>
</ng-template>
</goabx-modal>const modal = document.getElementById('delete-modal');
const deleteBtn = document.getElementById('delete-btn');
const cancelBtn = document.getElementById('cancel-btn');
const confirmDeleteBtn = document.getElementById('confirm-delete-btn');
deleteBtn.addEventListener('_click', () => {
modal.setAttribute('open', 'true');
});
cancelBtn.addEventListener('_click', () => {
modal.removeAttribute('open');
});
confirmDeleteBtn.addEventListener('_click', () => {
modal.removeAttribute('open');
});
modal.addEventListener('_close', () => {
modal.removeAttribute('open');
});<goa-button version="2" type="tertiary" leadingicon="trash" id="delete-btn">Delete record</goa-button>
<goa-modal version="2" id="delete-modal" heading="Are you sure you want to delete this record?">
<p>This action cannot be undone.</p>
<div slot="actions">
<goa-button-group alignment="end">
<goa-button version="2" type="tertiary" size="compact" id="cancel-btn">Cancel</goa-button>
<goa-button version="2" type="primary" variant="destructive" size="compact" id="confirm-delete-btn">Delete record</goa-button>
</goa-button-group>
</div>
</goa-modal>Confirm before navigating away
const [open, setOpen] = useState(false);
const handleChangeRoute = () => {
setOpen(false);
// In a real app, you would use your router's navigate function
// setTimeout(() => navigate("/some-path"), 300);
console.log("Navigating to new route...");
};<GoabxButton onClick={() => setOpen(true)}>Open</GoabxButton>
<GoabxModal
heading="Are you sure you want to change route?"
open={open}
onClose={() => setOpen(false)}
actions={
<GoabButtonGroup alignment="end">
<GoabxButton type="secondary" size="compact" onClick={() => setOpen(false)}>
Cancel
</GoabxButton>
<GoabxButton type="primary" size="compact" onClick={handleChangeRoute}>
Change route
</GoabxButton>
</GoabButtonGroup>
}
/>open = false;
constructor(private router: Router) {}
onOpen(): void {
this.open = true;
}
onClose(): void {
this.open = false;
}
onChangeRoute(): void {
this.open = false;
// setTimeout will allow any modal transitions to be run
setTimeout(() => this.router.navigate(["/components"]), 0);
}<goabx-button (onClick)="onOpen()">Open</goabx-button>
<goabx-modal [open]="open" heading="Are you sure you want to change route?" [actions]="actions">
<ng-template #actions>
<goab-button-group alignment="end">
<goabx-button type="secondary" size="compact" (onClick)="onClose()">Cancel</goabx-button>
<goabx-button type="primary" size="compact" (onClick)="onChangeRoute()">Change route</goabx-button>
</goab-button-group>
</ng-template>
</goabx-modal>const modal = document.getElementById('route-modal');
const openBtn = document.getElementById('open-btn');
const cancelBtn = document.getElementById('cancel-btn');
const changeRouteBtn = document.getElementById('change-route-btn');
openBtn.addEventListener('_click', () => {
modal.setAttribute('open', 'true');
});
cancelBtn.addEventListener('_click', () => {
modal.removeAttribute('open');
});
changeRouteBtn.addEventListener('_click', () => {
modal.removeAttribute('open');
// setTimeout will allow any modal transitions to be run
setTimeout(() => {
window.location.href = '/components';
}, 300);
});
modal.addEventListener('_close', () => {
modal.removeAttribute('open');
});<goa-button version="2" id="open-btn">Open</goa-button>
<goa-modal version="2" id="route-modal" heading="Are you sure you want to change route?">
<div slot="actions">
<goa-button-group alignment="end">
<goa-button version="2" type="secondary" size="compact" id="cancel-btn">Cancel</goa-button>
<goa-button version="2" type="primary" size="compact" id="change-route-btn">Change route</goa-button>
</goa-button-group>
</div>
</goa-modal>Require user action before continuing
const [open, setOpen] = useState(false);<GoabxButton onClick={() => setOpen(true)}>Open Basic Modal</GoabxButton>
<GoabxModal
heading="Are you sure you want to continue?"
open={open}
onClose={() => setOpen(false)}
actions={
<GoabButtonGroup alignment="end">
<GoabxButton type="secondary" size="compact" onClick={() => setOpen(false)}>
Back
</GoabxButton>
<GoabxButton type="primary" size="compact" onClick={() => setOpen(false)}>
Continue
</GoabxButton>
</GoabButtonGroup>
}
>
<p>You cannot return to this page.</p>
</GoabxModal>open = false;
toggleModal(): void {
this.open = !this.open;
}<goabx-button (onClick)="toggleModal()">Open Basic Modal</goabx-button>
<goabx-modal
[open]="open"
(onClose)="toggleModal()"
heading="Are you sure you want to continue?"
[actions]="actions">
<p>You cannot return to this page.</p>
<ng-template #actions>
<goab-button-group alignment="end">
<goabx-button type="secondary" size="compact" (onClick)="toggleModal()">Back</goabx-button>
<goabx-button type="primary" size="compact" (onClick)="toggleModal()">Continue</goabx-button>
</goab-button-group>
</ng-template>
</goabx-modal>const modal = document.getElementById("confirmation-modal");
const openBtn = document.getElementById("open-modal-btn");
const backBtn = document.getElementById("back-btn");
const continueBtn = document.getElementById("continue-btn");
function openModal() {
modal.setAttribute("open", "true");
}
function closeModal() {
modal.removeAttribute("open");
}
openBtn.addEventListener("_click", openModal);
backBtn.addEventListener("_click", closeModal);
continueBtn.addEventListener("_click", closeModal);
modal.addEventListener("_close", closeModal);<goa-button version="2" id="open-modal-btn">Open Basic Modal</goa-button>
<goa-modal version="2" id="confirmation-modal" heading="Are you sure you want to continue?">
<p>You cannot return to this page.</p>
<div slot="actions">
<goa-button-group alignment="end">
<goa-button version="2" id="back-btn" type="secondary" size="compact">Back</goa-button>
<goa-button version="2" id="continue-btn" type="primary" size="compact">Continue</goa-button>
</goa-button-group>
</div>
</goa-modal>Warn a user of a deadline
const [open, setOpen] = useState(false);<GoabxButton type="secondary" onClick={() => setOpen(true)}>
Save for later
</GoabxButton>
<GoabxModal
heading="Complete submission prior to 1PM"
calloutVariant="important"
open={open}
onClose={() => setOpen(false)}
actions={
<GoabButtonGroup alignment="end">
<GoabxButton type="primary" onClick={() => setOpen(false)}>
I understand
</GoabxButton>
</GoabButtonGroup>
}
>
<p>
You've selected to adjourn a matter that is required to appear today. This Calgary court
location does not accept adjournment requests past 1PM MST. Please submit your
adjournment request as soon as possible.
</p>
</GoabxModal>
);
}open = false;
toggleModal(): void {
this.open = !this.open;
}<goabx-button type="secondary" (onClick)="toggleModal()">Save for later</goabx-button>
<goabx-modal
[open]="open"
calloutVariant="important"
(onClose)="toggleModal()"
heading="Complete submission prior to 1PM"
[actions]="actions">
<p>
You've selected to adjourn a matter that is required to appear today. This Calgary court
location does not accept adjournment requests past 1PM MST. Please submit your adjournment
request as soon as possible.
</p>
<ng-template #actions>
<goab-button-group alignment="end">
<goabx-button type="primary" (onClick)="toggleModal()">I understand</goabx-button>
</goab-button-group>
</ng-template>
</goabx-modal>const modal = document.getElementById("deadline-modal");
const openBtn = document.getElementById("open-modal-btn");
const understandBtn = document.getElementById("understand-btn");
openBtn.addEventListener("_click", () => {
modal.setAttribute("open", "true");
});
understandBtn.addEventListener("_click", () => {
modal.removeAttribute("open");
});
modal.addEventListener("_close", () => {
modal.removeAttribute("open");
});<goa-button version="2" type="secondary" id="open-modal-btn">Save for later</goa-button>
<goa-modal version="2"
id="deadline-modal"
calloutvariant="important"
heading="Complete submission prior to 1PM">
You've selected to adjourn a matter that is required to appear today. This Calgary court
location does not accept adjournment requests past 1PM MST. Please submit your adjournment
request as soon as possible.
<div slot="actions">
<goa-button-group alignment="end">
<goa-button version="2" type="primary" size="compact" id="understand-btn">I understand</goa-button>
</goa-button-group>
</div>
</goa-modal>Content
Do
Use descriptive language in both modal content and button text to inform users of the resulting destructive action.
Submitting the assessment will inform the proponent and email a copy of the report.
Do
Use a concise and descriptive modal title that spans less than one line.
Are you sure that you want to cancel your application? This action will permanently cancel your application and delete any information collected.
Do
Use descriptive language in content and button text for destructive actions.
Types
Do
Use the destructive button variant for actions that cannot be easily undone, like permanently deleting data or removing a user from a system.
Don't
Don't use a destructive button to trigger a confirmation. Reserve destructive styling for the final action inside the modal.