@lion/ui 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/button/src/LionButton.js +5 -1
- package/components/button/test-suites/LionButton.suite.js +1 -1
- package/components/calendar/src/LionCalendar.js +5 -14
- package/components/calendar/test/lion-calendar.test.js +88 -0
- package/components/calendar/translations/bg.js +5 -0
- package/components/calendar/translations/cs.js +5 -0
- package/components/calendar/translations/de.js +5 -0
- package/components/calendar/translations/en.js +5 -0
- package/components/calendar/translations/es.js +5 -0
- package/components/calendar/translations/fr.js +5 -0
- package/components/calendar/translations/hu.js +5 -0
- package/components/calendar/translations/it.js +5 -0
- package/components/calendar/translations/nl.js +5 -0
- package/components/calendar/translations/pl.js +5 -0
- package/components/calendar/translations/ro.js +5 -0
- package/components/calendar/translations/ru.js +5 -0
- package/components/calendar/translations/sk.js +5 -0
- package/components/calendar/translations/uk.js +3 -0
- package/components/calendar/translations/zh.js +3 -0
- package/components/core/src/SlotMixin.js +15 -0
- package/components/core/test/SlotMixin.test.js +50 -0
- package/components/dialog/src/LionDialog.js +13 -0
- package/components/dialog/test/lion-dialog.test.js +56 -0
- package/components/form-core/src/choice-group/ChoiceInputMixin.js +3 -1
- package/components/input-tel-dropdown/src/LionInputTelDropdown.js +1 -1
- package/components/overlays/src/OverlayController.js +17 -1
- package/components/overlays/test/OverlayController.test.js +29 -0
- package/components/overlays/types/OverlayConfig.ts +4 -2
- package/custom-elements.json +38 -13
- package/dist-types/components/button/src/LionButton.d.ts.map +1 -1
- package/dist-types/components/calendar/src/LionCalendar.d.ts.map +1 -1
- package/dist-types/components/calendar/translations/bg.d.ts +3 -0
- package/dist-types/components/calendar/translations/cs.d.ts +3 -0
- package/dist-types/components/calendar/translations/de.d.ts +3 -0
- package/dist-types/components/calendar/translations/en.d.ts +3 -0
- package/dist-types/components/calendar/translations/es.d.ts +3 -0
- package/dist-types/components/calendar/translations/fr.d.ts +3 -0
- package/dist-types/components/calendar/translations/hu.d.ts +3 -0
- package/dist-types/components/calendar/translations/it.d.ts +3 -0
- package/dist-types/components/calendar/translations/nl.d.ts +3 -0
- package/dist-types/components/calendar/translations/pl.d.ts +3 -0
- package/dist-types/components/calendar/translations/ro.d.ts +3 -0
- package/dist-types/components/calendar/translations/ru.d.ts +3 -0
- package/dist-types/components/calendar/translations/sk.d.ts +3 -0
- package/dist-types/components/calendar/translations/uk.d.ts +3 -0
- package/dist-types/components/calendar/translations/zh.d.ts +3 -0
- package/dist-types/components/core/src/SlotMixin.d.ts.map +1 -1
- package/dist-types/components/dialog/src/LionDialog.d.ts +32 -0
- package/dist-types/components/dialog/src/LionDialog.d.ts.map +1 -1
- package/dist-types/components/form-core/src/choice-group/ChoiceInputMixin.d.ts.map +1 -1
- package/dist-types/components/input-tel-dropdown/src/LionInputTelDropdown.d.ts +1 -1
- package/dist-types/components/overlays/src/OverlayController.d.ts +5 -0
- package/dist-types/components/overlays/src/OverlayController.d.ts.map +1 -1
- package/dist-types/components/overlays/types/OverlayConfig.d.ts +4 -2
- package/dist-types/components/overlays/types/OverlayConfig.d.ts.map +1 -1
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts +1 -0
- package/dist-types/components/select-rich/src/LionSelectRich.d.ts.map +1 -1
- package/dist-types/tsconfig.tsbuildinfo +1 -1
- package/docs/components/checkbox-group/overview.md +2 -2
- package/docs/components/collapsible/overview.md +1 -1
- package/docs/components/combobox/overview.md +1 -1
- package/docs/components/combobox/use-cases.md +1 -1
- package/docs/components/dialog/api-table.md +5 -3
- package/docs/components/dialog/overview.md +1 -1
- package/docs/components/dialog/use-cases.md +32 -1
- package/docs/components/fieldset/overview.md +5 -5
- package/docs/components/form/overview.md +3 -3
- package/docs/components/form/use-cases.md +2 -2
- package/docs/components/icon/use-cases.md +1 -1
- package/docs/components/input/overview.md +1 -1
- package/docs/components/input-amount/overview.md +3 -3
- package/docs/components/input-date/overview.md +3 -3
- package/docs/components/input-datepicker/overview.md +5 -5
- package/docs/components/input-email/overview.md +2 -2
- package/docs/components/input-file/overview.md +1 -1
- package/docs/components/input-iban/overview.md +2 -2
- package/docs/components/input-range/overview.md +2 -2
- package/docs/components/input-stepper/overview.md +1 -1
- package/docs/components/input-tel/overview.md +1 -1
- package/docs/components/input-tel/use-cases.md +6 -6
- package/docs/components/input-tel-dropdown/overview.md +1 -1
- package/docs/components/listbox/use-cases.md +1 -1
- package/docs/components/radio-group/overview.md +3 -3
- package/docs/components/select/use-cases.md +1 -1
- package/docs/components/select-rich/use-cases.md +1 -1
- package/docs/components/tabs/examples.md +1 -1
- package/docs/fundamentals/index.md +4 -4
- package/docs/fundamentals/node-tools/publish-docs/overview.md +3 -3
- package/docs/fundamentals/node-tools/rocket-preset-extend-lion-docs/overview.md +2 -2
- package/docs/fundamentals/rationales/accessibility.md +1 -1
- package/docs/fundamentals/systems/core/SlotMixin.md +1 -1
- package/docs/fundamentals/systems/core/overview.md +1 -1
- package/docs/fundamentals/systems/form/formatting-and-parsing.md +4 -4
- package/docs/fundamentals/systems/form/model-value.md +1 -1
- package/docs/fundamentals/systems/form/overview.md +28 -28
- package/docs/fundamentals/systems/form/validate.md +2 -2
- package/docs/fundamentals/systems/localize/overview.md +4 -4
- package/docs/fundamentals/systems/overlays/api-table.md +35 -34
- package/docs/fundamentals/systems/overlays/configuration.md +32 -2
- package/docs/fundamentals/systems/overlays/overview.md +5 -5
- package/docs/fundamentals/systems/overlays/use-cases.md +2 -2
- package/docs/fundamentals/tools/helpers/overview.md +1 -1
- package/docs/fundamentals/tools/singleton-manager/overview.md +4 -4
- package/docs/guides/how-to/create-a-custom-field.md +5 -5
- package/docs/guides/how-to/create-a-form.md +10 -9
- package/docs/guides/how-to/extend-a-native-input.md +2 -2
- package/docs/guides/index.md +8 -8
- package/docs/guides/principles/index.md +5 -5
- package/docs/guides/principles/styling.md +1 -1
- package/package.json +1 -1
|
@@ -149,7 +149,11 @@ export class LionButton extends DisabledWithTabIndexMixin(LitElement) {
|
|
|
149
149
|
super.updated(changedProperties);
|
|
150
150
|
|
|
151
151
|
if (changedProperties.has('disabled')) {
|
|
152
|
-
|
|
152
|
+
if (this.disabled) {
|
|
153
|
+
this.setAttribute('aria-disabled', 'true');
|
|
154
|
+
} else if (this.getAttribute('aria-disabled') !== null) {
|
|
155
|
+
this.removeAttribute('aria-disabled');
|
|
156
|
+
}
|
|
153
157
|
}
|
|
154
158
|
}
|
|
155
159
|
|
|
@@ -40,7 +40,7 @@ export function LionButtonSuite({ klass = LionButton } = {}) {
|
|
|
40
40
|
el.disabled = false;
|
|
41
41
|
await el.updateComplete;
|
|
42
42
|
expect(el.getAttribute('tabindex')).to.equal('0');
|
|
43
|
-
expect(el.getAttribute('aria-disabled')).to.
|
|
43
|
+
expect(el.getAttribute('aria-disabled')).to.not.exist;
|
|
44
44
|
expect(el.hasAttribute('disabled')).to.equal(false);
|
|
45
45
|
|
|
46
46
|
el.disabled = true;
|
|
@@ -658,25 +658,16 @@ export class LionCalendar extends LocalizeMixin(LitElement) {
|
|
|
658
658
|
day.ariaCurrent = day.today ? 'date' : undefined;
|
|
659
659
|
day.disabledInfo = '';
|
|
660
660
|
|
|
661
|
+
if (day.disabled) {
|
|
662
|
+
day.disabledInfo = `${this.msgLit(`lion-calendar:defaultDisabledDate`)}`;
|
|
663
|
+
}
|
|
661
664
|
if (this.minDate && normalizeDateTime(day.date) < normalizeDateTime(this.minDate)) {
|
|
662
665
|
day.disabled = true;
|
|
663
|
-
|
|
664
|
-
day.disabledInfo = `This date is unavailable. Earliest date to select is ${
|
|
665
|
-
this.__getSelectableDateRange().earliestSelectableDate
|
|
666
|
-
}. Please select another date.`;
|
|
666
|
+
day.disabledInfo = `${this.msgLit(`lion-calendar:beforeDisabledDate`, { params: this.__getSelectableDateRange().earliestSelectableDate })}`;
|
|
667
667
|
}
|
|
668
|
-
|
|
669
668
|
if (this.maxDate && normalizeDateTime(day.date) > normalizeDateTime(this.maxDate)) {
|
|
670
669
|
day.disabled = true;
|
|
671
|
-
|
|
672
|
-
day.disabledInfo = `This date is unavailable. Latest date to select is ${
|
|
673
|
-
this.__getSelectableDateRange().latestSelectableDate
|
|
674
|
-
}. Please select another date.`;
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
if (day.disabled) {
|
|
678
|
-
// TODO: turn this into a translated string
|
|
679
|
-
day.disabledInfo = `This date is unavailable. Please select another date`;
|
|
670
|
+
day.disabledInfo = `${this.msgLit(`lion-calendar:afterDisabledDate`, { params: this.__getSelectableDateRange().latestSelectableDate })}`;
|
|
680
671
|
}
|
|
681
672
|
|
|
682
673
|
return this.dayPreprocessor(day);
|
|
@@ -896,6 +896,94 @@ describe('<lion-calendar>', () => {
|
|
|
896
896
|
'Next year, December 2001',
|
|
897
897
|
);
|
|
898
898
|
});
|
|
899
|
+
|
|
900
|
+
it('disables a date with disableDates function provide a correct message', async () => {
|
|
901
|
+
/** @param {Date} d */
|
|
902
|
+
const disable15th = d => d.getDate() === 15;
|
|
903
|
+
const el = await fixture(html`
|
|
904
|
+
<lion-calendar
|
|
905
|
+
.selectedDate="${new Date('2000/12/01')}"
|
|
906
|
+
.disableDates=${disable15th}
|
|
907
|
+
></lion-calendar>
|
|
908
|
+
`);
|
|
909
|
+
const elObj = new CalendarObject(el);
|
|
910
|
+
expect(elObj.getDayObj(15).isDisabled).to.equal(true);
|
|
911
|
+
expect(elObj.getDayObj(15).el).dom.to.equal(`
|
|
912
|
+
<div
|
|
913
|
+
class="calendar__day-button"
|
|
914
|
+
aria-disabled="true"
|
|
915
|
+
role="button"
|
|
916
|
+
tabindex="-1"
|
|
917
|
+
aria-pressed="false"
|
|
918
|
+
past=""
|
|
919
|
+
current-month="">
|
|
920
|
+
<span class="calendar__day-button__text">
|
|
921
|
+
15
|
|
922
|
+
</span>
|
|
923
|
+
<span class="u-sr-only">
|
|
924
|
+
December 2000 Friday This date is unavailable. Please choose another date.
|
|
925
|
+
</span>
|
|
926
|
+
</div>
|
|
927
|
+
`);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it('disables days before "minDate" property provide a correct message', async () => {
|
|
931
|
+
const el = await fixture(html`
|
|
932
|
+
<lion-calendar
|
|
933
|
+
.selectedDate="${new Date('2000/12/31')}"
|
|
934
|
+
.minDate="${new Date('2000/12/09')}"
|
|
935
|
+
>
|
|
936
|
+
</lion-calendar>
|
|
937
|
+
`);
|
|
938
|
+
const elObj = new CalendarObject(el);
|
|
939
|
+
expect(elObj.getDayObj(1).isDisabled).to.equal(true);
|
|
940
|
+
expect(elObj.getDayObj(1).el).dom.to.equal(`
|
|
941
|
+
<div
|
|
942
|
+
class="calendar__day-button"
|
|
943
|
+
aria-disabled="true"
|
|
944
|
+
role="button"
|
|
945
|
+
tabindex="-1"
|
|
946
|
+
aria-pressed="false"
|
|
947
|
+
past=""
|
|
948
|
+
current-month="">
|
|
949
|
+
<span class="calendar__day-button__text">
|
|
950
|
+
1
|
|
951
|
+
</span>
|
|
952
|
+
<span class="u-sr-only">
|
|
953
|
+
December 2000 Friday This date is unavailable. Earliest available date is 9 December 2000. Please choose another date.
|
|
954
|
+
</span>
|
|
955
|
+
</div>
|
|
956
|
+
`);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it('disables days after "maxDate" property provide a correct message', async () => {
|
|
960
|
+
const el = await fixture(html`
|
|
961
|
+
<lion-calendar
|
|
962
|
+
.selectedDate="${new Date('2000/12/31')}"
|
|
963
|
+
.maxDate="${new Date('2000/12/09')}"
|
|
964
|
+
>
|
|
965
|
+
</lion-calendar>
|
|
966
|
+
`);
|
|
967
|
+
const elObj = new CalendarObject(el);
|
|
968
|
+
expect(elObj.getDayObj(30).isDisabled).to.equal(true);
|
|
969
|
+
expect(elObj.getDayObj(30).el).dom.to.equal(`
|
|
970
|
+
<div
|
|
971
|
+
class="calendar__day-button"
|
|
972
|
+
aria-disabled="true"
|
|
973
|
+
role="button"
|
|
974
|
+
tabindex="-1"
|
|
975
|
+
aria-pressed="false"
|
|
976
|
+
past=""
|
|
977
|
+
current-month="">
|
|
978
|
+
<span class="calendar__day-button__text">
|
|
979
|
+
30
|
|
980
|
+
</span>
|
|
981
|
+
<span class="u-sr-only">
|
|
982
|
+
December 2000 Saturday This date is unavailable. Latest available date is 9 December 2000. Please choose another date.
|
|
983
|
+
</span>
|
|
984
|
+
</div>
|
|
985
|
+
`);
|
|
986
|
+
});
|
|
899
987
|
});
|
|
900
988
|
});
|
|
901
989
|
});
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Предишен месец',
|
|
4
4
|
nextFullYear: 'Следващ година',
|
|
5
5
|
previousFullYear: 'Предишен година',
|
|
6
|
+
defaultDisabledDate: 'Тази дата не е налична. Моля, изберете друга дата.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Тази дата не е налична. Най-ранната налична да е {params}. Моля, изберете друга дата.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Тази дата не е налична. Най-късната налична дата е {params}. Моля, изберете друга дата.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Předchozí měsíc',
|
|
4
4
|
nextFullYear: 'Příští rok',
|
|
5
5
|
previousFullYear: 'Předchozí rok',
|
|
6
|
+
defaultDisabledDate: 'Toto datum není k dispozici. Vyberte jiné datum.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Toto datum není k dispozici. Nejbližší dostupné datum je {params}. Vyberte jiné datum.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Toto datum není k dispozici. Poslední dostupné datum je {params}. Vyberte jiné datum.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Vorheriger Monat',
|
|
4
4
|
nextFullYear: 'Nächstes Jahr',
|
|
5
5
|
previousFullYear: 'Vorheriges Jahr',
|
|
6
|
+
defaultDisabledDate: 'Dieses Datum ist nicht verfügbar. Bitte wählen Sie ein anderes Datum aus.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Dieses Datum ist nicht verfügbar. Das früheste verfügbare Datum ist {params}. Bitte wählen Sie ein anderes Datum aus.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Dieses Datum ist nicht verfügbar. Das späteste verfügbare Datum ist {params}. Bitte wählen Sie ein anderes Datum aus.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Previous month',
|
|
4
4
|
nextFullYear: 'Next year',
|
|
5
5
|
previousFullYear: 'Previous year',
|
|
6
|
+
defaultDisabledDate: 'This date is unavailable. Please choose another date.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'This date is unavailable. Earliest available date is {params}. Please choose another date.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'This date is unavailable. Latest available date is {params}. Please choose another date.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Mes anterior',
|
|
4
4
|
nextFullYear: 'Año siguiente',
|
|
5
5
|
previousFullYear: 'Año anterior',
|
|
6
|
+
defaultDisabledDate: 'Esta fecha no está disponible. Elija otra.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Esta fecha no está disponible. La fecha más próxima disponible es {params}. Elija otra.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Esta fecha no está disponible. La fecha más lejana disponible es {params}. Elija otra.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Mois précédent',
|
|
4
4
|
nextFullYear: 'An prochain',
|
|
5
5
|
previousFullYear: 'An précédent',
|
|
6
|
+
defaultDisabledDate: "Cette date n'est pas disponible. Veuillez choisir une autre date.",
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
"Cette date n'est pas disponible. La prochaine date disponible est la suivante : {params}. Veuillez choisir une autre date.",
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
"Cette date n'est pas disponible. La dernière date disponible est la suivante : {params}. Veuillez choisir une autre date.",
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Előző hónap',
|
|
4
4
|
nextFullYear: 'Következő év',
|
|
5
5
|
previousFullYear: 'Előző év',
|
|
6
|
+
defaultDisabledDate: 'Ez a dátum nem választható. Válasszon másik dátumot.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Ez a dátum nem választható. A legkorábbi elérhető dátum {params}. Válasszon másik dátumot.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Ez a dátum nem választható. A legkésőbbi elérhető dátum {params}. Válasszon másik dátumot.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Mese precedente',
|
|
4
4
|
nextFullYear: 'Anno successivo',
|
|
5
5
|
previousFullYear: 'Anno precedente',
|
|
6
|
+
defaultDisabledDate: "Questa data non è disponibile. Scegline un'altra.",
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
"Questa data non è disponibile. La prima data disponibile è {params}. Scegline un'altra.",
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
"Questa data non è disponibile. L'ultima data disponibile è {params}. Scegline un'altra.",
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Vorige maand',
|
|
4
4
|
nextFullYear: 'Volgend jaar',
|
|
5
5
|
previousFullYear: 'Vorig jaar',
|
|
6
|
+
defaultDisabledDate: 'Deze datum is niet beschikbaar. Kies een andere datum.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Deze datum is niet beschikbaar. Vroegste beschikbare datum is {params}. Kies een andere datum.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Deze datum is niet beschikbaar. Laatste beschikbare datum is {params}. Kies een andere datum.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Poprzedni miesiąc',
|
|
4
4
|
nextFullYear: 'Następny rok',
|
|
5
5
|
previousFullYear: 'Poprzedni rok',
|
|
6
|
+
defaultDisabledDate: 'Ta data jest niedostępna. Wybierz inną datę.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Ta data jest niedostępna. Najwcześniejsza dostępna data to {params}. Wybierz inną datę.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Ta data jest niedostępna. Najpóźniejsza dostępna data to {params}. Wybierz inną datę.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Luna anterioară',
|
|
4
4
|
nextFullYear: 'An viitoare',
|
|
5
5
|
previousFullYear: 'An anterioară',
|
|
6
|
+
defaultDisabledDate: 'Această dată nu este disponibilă. Vă rugăm să alegeți altă dată.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Această dată nu este disponibilă. Cea mai apropiată dată disponibilă este {params}. Vă rugăm să alegeți altă dată.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Această dată nu este disponibilă. Ultima dată disponibilă este {params}. Vă rugăm să alegeți altă dată.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Предыдущий месяц',
|
|
4
4
|
nextFullYear: 'Следующий год',
|
|
5
5
|
previousFullYear: 'Предыдущий год',
|
|
6
|
+
defaultDisabledDate: 'Эта дата недоступна. Выберите другую дату.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Эта дата недоступна. Самая ранняя доступная дата — {params}. Выберите другую дату.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Эта дата недоступна. Самая поздняя доступная дата — {params}. Выберите другую дату.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,9 @@ export default {
|
|
|
3
3
|
previousMonth: 'Predchádzajúci mesiac',
|
|
4
4
|
nextFullYear: 'Nasledujúci rok',
|
|
5
5
|
previousFullYear: 'Predchádzajúci rok',
|
|
6
|
+
defaultDisabledDate: 'Tento dátum nie je k dispozícii. Vyberte iný dátum.',
|
|
7
|
+
beforeDisabledDate:
|
|
8
|
+
'Tento dátum nie je k dispozícii. Najbližší možný dátum je {params}. Vyberte iný dátum.',
|
|
9
|
+
afterDisabledDate:
|
|
10
|
+
'Tento dátum nie je k dispozícii. Posledný možný dátum je {params}. Vyberte iný dátum.',
|
|
6
11
|
};
|
|
@@ -3,4 +3,7 @@ export default {
|
|
|
3
3
|
previousMonth: 'Попередній місяць',
|
|
4
4
|
nextFullYear: 'Наступний року',
|
|
5
5
|
previousFullYear: 'Попередній року',
|
|
6
|
+
defaultDisabledDate: 'Ця дата недоступна. Виберіть іншу дату.',
|
|
7
|
+
beforeDisabledDate: 'Ця дата недоступна. Найближча доступна дата {params}. Виберіть іншу дату.',
|
|
8
|
+
afterDisabledDate: 'Ця дата недоступна. Остання доступна дата {params}. Виберіть іншу дату.',
|
|
6
9
|
};
|
|
@@ -182,6 +182,21 @@ const SlotMixinImplementation = superclass =>
|
|
|
182
182
|
// Providing all options breaks Safari: we keep host and creationScope
|
|
183
183
|
const { creationScope, host } = this.renderOptions;
|
|
184
184
|
render(template, rerenderTarget, { creationScope, host, renderBefore });
|
|
185
|
+
|
|
186
|
+
// Assume we had this config:
|
|
187
|
+
// `'my-slot': () => ({ template: myBool ? html`<div id=a></div>` : html`<span id=b></span>`, renderAsDirectHostChild: true })`
|
|
188
|
+
// If myBool started as true, <div id=a></div> would be rendered in first render above, a slot would be applied,
|
|
189
|
+
// resulting in <div id=a slot=my-slot></div>
|
|
190
|
+
// However, when myBool changes to false, the <span id=b></span> would be rendered as root instead...
|
|
191
|
+
// We need to make sure that this "replaced root" gets the slot applied as well => <span id=b slot=my-slot></span>
|
|
192
|
+
const isRerenderingRootOfTemplate =
|
|
193
|
+
renderAsDirectHostChild &&
|
|
194
|
+
renderBefore.previousElementSibling &&
|
|
195
|
+
!renderBefore.previousElementSibling.slot;
|
|
196
|
+
|
|
197
|
+
if (isRerenderingRootOfTemplate) {
|
|
198
|
+
renderBefore.previousElementSibling.slot = slotName;
|
|
199
|
+
}
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
/**
|
|
@@ -345,6 +345,56 @@ describe('SlotMixin', () => {
|
|
|
345
345
|
expect(isActiveElement(el._focusableNode._buttonNode, { deep: true })).to.be.true;
|
|
346
346
|
});
|
|
347
347
|
|
|
348
|
+
it('allows for switching template root in slot as a direct child', async () => {
|
|
349
|
+
const tagName = defineCE(
|
|
350
|
+
// @ts-expect-error
|
|
351
|
+
class extends SlotMixin(LitElement) {
|
|
352
|
+
static properties = { isSwitched: Boolean };
|
|
353
|
+
|
|
354
|
+
constructor() {
|
|
355
|
+
super();
|
|
356
|
+
this.isSwitched = false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
get slots() {
|
|
360
|
+
return {
|
|
361
|
+
...super.slots,
|
|
362
|
+
'my-root-switcher-node': () => ({
|
|
363
|
+
template: this.isSwitched
|
|
364
|
+
? html`<div id="is-switched"></div>`
|
|
365
|
+
: html`<span id="is-not-switched"> </span> `,
|
|
366
|
+
renderAsDirectHostChild: true,
|
|
367
|
+
}),
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
render() {
|
|
372
|
+
return html`<slot name="my-root-switcher-node"></slot>`;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
get _myRootSwitcherNode() {
|
|
376
|
+
return /** @type HTMLSpanElement */ (
|
|
377
|
+
Array.from(this.children).find(elm => elm.slot === 'my-root-switcher-node')
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
);
|
|
382
|
+
const el = /** @type {* & SlotHost} */ (await fixture(`<${tagName}></${tagName}>`));
|
|
383
|
+
|
|
384
|
+
expect(el._myRootSwitcherNode.id).to.equal('is-not-switched');
|
|
385
|
+
expect(el.innerHTML).to.equal(
|
|
386
|
+
`<!--_start_slot_my-root-switcher-node_--><!----><span id="is-not-switched" slot="my-root-switcher-node"> </span> <!--_end_slot_my-root-switcher-node_-->`,
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
el.isSwitched = true;
|
|
390
|
+
await el.updateComplete;
|
|
391
|
+
|
|
392
|
+
expect(el._myRootSwitcherNode.id).to.equal('is-switched');
|
|
393
|
+
expect(el.innerHTML).to.equal(
|
|
394
|
+
`<!--_start_slot_my-root-switcher-node_--><!----><div id="is-switched" slot="my-root-switcher-node"></div><!--_end_slot_my-root-switcher-node_-->`,
|
|
395
|
+
);
|
|
396
|
+
});
|
|
397
|
+
|
|
348
398
|
describe('firstRenderOnConnected (for backwards compatibility)', () => {
|
|
349
399
|
it('does render on connected when firstRenderOnConnected:true', async () => {
|
|
350
400
|
// Start with elem that does not render on connectedCallback
|
|
@@ -2,6 +2,18 @@ import { html, LitElement } from 'lit';
|
|
|
2
2
|
import { OverlayMixin, withModalDialogConfig } from '@lion/ui/overlays.js';
|
|
3
3
|
|
|
4
4
|
export class LionDialog extends OverlayMixin(LitElement) {
|
|
5
|
+
/** @type {any} */
|
|
6
|
+
static get properties() {
|
|
7
|
+
return {
|
|
8
|
+
isAlertDialog: { type: Boolean, attribute: 'is-alert-dialog' },
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
super();
|
|
14
|
+
this.isAlertDialog = false;
|
|
15
|
+
}
|
|
16
|
+
|
|
5
17
|
/**
|
|
6
18
|
* @protected
|
|
7
19
|
*/
|
|
@@ -9,6 +21,7 @@ export class LionDialog extends OverlayMixin(LitElement) {
|
|
|
9
21
|
_defineOverlayConfig() {
|
|
10
22
|
return {
|
|
11
23
|
...withModalDialogConfig(),
|
|
24
|
+
isAlertDialog: this.isAlertDialog,
|
|
12
25
|
};
|
|
13
26
|
}
|
|
14
27
|
|
|
@@ -170,6 +170,27 @@ describe('lion-dialog', () => {
|
|
|
170
170
|
});
|
|
171
171
|
|
|
172
172
|
describe('Accessibility', () => {
|
|
173
|
+
it('passes a11y audit', async () => {
|
|
174
|
+
const el = await fixture(html`
|
|
175
|
+
<lion-dialog>
|
|
176
|
+
<button slot="invoker">Invoker</button>
|
|
177
|
+
<div slot="content" class="dialog" aria-label="Dialog">Hey there</div>
|
|
178
|
+
</lion-dialog>
|
|
179
|
+
`);
|
|
180
|
+
await expect(el).to.be.accessible();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('passes a11y audit when opened', async () => {
|
|
184
|
+
const el = await fixture(html`
|
|
185
|
+
<lion-dialog opened>
|
|
186
|
+
<button slot="invoker">Invoker</button>
|
|
187
|
+
<div slot="content" class="dialog" aria-label="Dialog">Hey there</div>
|
|
188
|
+
</lion-dialog>
|
|
189
|
+
`);
|
|
190
|
+
// error expected since we put role="none" on the dialog itself, which is valid but not recognized by Axe
|
|
191
|
+
await expect(el).to.be.accessible({ ignoredRules: ['aria-allowed-role'] });
|
|
192
|
+
});
|
|
193
|
+
|
|
173
194
|
it('does not add [aria-expanded] to invoker button', async () => {
|
|
174
195
|
const el = await fixture(
|
|
175
196
|
html` <lion-dialog>
|
|
@@ -187,6 +208,41 @@ describe('lion-dialog', () => {
|
|
|
187
208
|
await aTimeout(0);
|
|
188
209
|
expect(invokerButton.getAttribute('aria-expanded')).to.equal(null);
|
|
189
210
|
});
|
|
211
|
+
|
|
212
|
+
it('has role="dialog" by default', async () => {
|
|
213
|
+
const el = await fixture(
|
|
214
|
+
html` <lion-dialog>
|
|
215
|
+
<div slot="content" class="dialog">Hey there</div>
|
|
216
|
+
<button slot="invoker">Popup button</button>
|
|
217
|
+
</lion-dialog>`,
|
|
218
|
+
);
|
|
219
|
+
const contentNode = /** @type {HTMLElement} */ (el.querySelector('[slot="content"]'));
|
|
220
|
+
|
|
221
|
+
expect(contentNode.getAttribute('role')).to.equal('dialog');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it('has role="alertdialog" by when "is-alert-dialog" is set', async () => {
|
|
225
|
+
const el = await fixture(
|
|
226
|
+
html` <lion-dialog is-alert-dialog>
|
|
227
|
+
<div slot="content" class="dialog">Hey there</div>
|
|
228
|
+
<button slot="invoker">Popup button</button>
|
|
229
|
+
</lion-dialog>`,
|
|
230
|
+
);
|
|
231
|
+
const contentNode = /** @type {HTMLElement} */ (el.querySelector('[slot="content"]'));
|
|
232
|
+
|
|
233
|
+
expect(contentNode.getAttribute('role')).to.equal('alertdialog');
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('passes a11y audit when opened and role="alertdialog"', async () => {
|
|
237
|
+
const el = await fixture(html`
|
|
238
|
+
<lion-dialog opened is-alert-dialog>
|
|
239
|
+
<button slot="invoker">Invoker</button>
|
|
240
|
+
<div slot="content" class="dialog" aria-label="Dialog">Hey there</div>
|
|
241
|
+
</lion-dialog>
|
|
242
|
+
`);
|
|
243
|
+
// error expected since we put role="none" on the dialog itself, which is valid but not recognized by Axe
|
|
244
|
+
await expect(el).to.be.accessible({ ignoredRules: ['aria-allowed-role'] });
|
|
245
|
+
});
|
|
190
246
|
});
|
|
191
247
|
|
|
192
248
|
describe('Edge cases', () => {
|
|
@@ -171,7 +171,9 @@ const ChoiceInputMixinImplementation = superclass =>
|
|
|
171
171
|
render() {
|
|
172
172
|
return html`
|
|
173
173
|
<slot name="input"></slot>
|
|
174
|
-
<div class="choice-field__graphic-container"
|
|
174
|
+
<div class="choice-field__graphic-container" aria-hidden="true">
|
|
175
|
+
${this._choiceGraphicTemplate()}
|
|
176
|
+
</div>
|
|
175
177
|
<div class="choice-field__label">
|
|
176
178
|
<slot name="label"></slot>
|
|
177
179
|
</div>
|
|
@@ -151,6 +151,7 @@ export class OverlayController extends EventTarget {
|
|
|
151
151
|
hidesOnOutsideEsc: false,
|
|
152
152
|
hidesOnOutsideClick: false,
|
|
153
153
|
isTooltip: false,
|
|
154
|
+
isAlertDialog: false,
|
|
154
155
|
invokerRelation: 'description',
|
|
155
156
|
visibilityTriggerFunction: undefined,
|
|
156
157
|
handlesAccessibility: false,
|
|
@@ -381,6 +382,14 @@ export class OverlayController extends EventTarget {
|
|
|
381
382
|
return /** @type {boolean} */ (this.config?.isTooltip);
|
|
382
383
|
}
|
|
383
384
|
|
|
385
|
+
/**
|
|
386
|
+
* The alertdialog role is to be used on modal alert dialogs that interrupt a user's workflow
|
|
387
|
+
* to communicate an important message and require a response.
|
|
388
|
+
*/
|
|
389
|
+
get isAlertDialog() {
|
|
390
|
+
return /** @type {boolean} */ (this.config?.isAlertDialog);
|
|
391
|
+
}
|
|
392
|
+
|
|
384
393
|
/**
|
|
385
394
|
* By default, the tooltip content is a 'description' for the invoker (uses aria-describedby).
|
|
386
395
|
* Setting this property to 'label' makes the content function as a label (via aria-labelledby)
|
|
@@ -611,6 +620,10 @@ export class OverlayController extends EventTarget {
|
|
|
611
620
|
});
|
|
612
621
|
// @ts-ignore
|
|
613
622
|
wrappingDialogElement.open = true;
|
|
623
|
+
if (this.isTooltip) {
|
|
624
|
+
// needed to prevent tooltip getting focus in Safari and Firefox
|
|
625
|
+
wrappingDialogElement.setAttribute('tabindex', '-1');
|
|
626
|
+
}
|
|
614
627
|
|
|
615
628
|
this.__wrappingDialogNode.style.display = 'none';
|
|
616
629
|
this.contentWrapperNode.style.zIndex = '1';
|
|
@@ -672,7 +685,10 @@ export class OverlayController extends EventTarget {
|
|
|
672
685
|
if (this.invokerNode && !isModal) {
|
|
673
686
|
this.invokerNode.setAttribute('aria-expanded', `${this.isShown}`);
|
|
674
687
|
}
|
|
675
|
-
if (
|
|
688
|
+
if (this.isAlertDialog) {
|
|
689
|
+
this.contentNode.setAttribute('role', 'alertdialog');
|
|
690
|
+
} else if (!this.contentNode.getAttribute('role')) {
|
|
691
|
+
// N.B. if we did not explicitly set `isTooltip` or `isAlertDialog`, a role potentially already provided by a user (like 'listbox') takes precedence
|
|
676
692
|
this.contentNode.setAttribute('role', 'dialog');
|
|
677
693
|
}
|
|
678
694
|
}
|
|
@@ -1876,6 +1876,22 @@ describe('OverlayController', () => {
|
|
|
1876
1876
|
it.skip('adds attributes inert and aria-hidden="true" on all siblings of rootNode if an overlay is shown', async () => {});
|
|
1877
1877
|
it.skip('disables pointer events and selection on inert elements', async () => {});
|
|
1878
1878
|
|
|
1879
|
+
describe('Alert dialog', () => {
|
|
1880
|
+
it('sets role="alertdialog" when isAlertDialog is set', async () => {
|
|
1881
|
+
const invokerNode = /** @type {HTMLElement} */ (
|
|
1882
|
+
await fixture('<div role="button">invoker</div>')
|
|
1883
|
+
);
|
|
1884
|
+
const ctrl = new OverlayController({
|
|
1885
|
+
...withLocalTestConfig(),
|
|
1886
|
+
handlesAccessibility: true,
|
|
1887
|
+
isAlertDialog: true,
|
|
1888
|
+
invokerNode,
|
|
1889
|
+
});
|
|
1890
|
+
|
|
1891
|
+
expect(ctrl.contentNode?.getAttribute('role')).to.equal('alertdialog');
|
|
1892
|
+
});
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1879
1895
|
describe('Tooltip', () => {
|
|
1880
1896
|
it('adds [aria-describedby] on invoker', async () => {
|
|
1881
1897
|
const invokerNode = /** @type {HTMLElement} */ (
|
|
@@ -1922,6 +1938,19 @@ describe('OverlayController', () => {
|
|
|
1922
1938
|
expect(ctrl.contentNode.getAttribute('role')).to.equal('tooltip');
|
|
1923
1939
|
});
|
|
1924
1940
|
|
|
1941
|
+
it('adds tabindex="-1" to the wrappingDialog element', async () => {
|
|
1942
|
+
const invokerNode = /** @type {HTMLElement} */ (
|
|
1943
|
+
await fixture('<div role="button">invoker</div>')
|
|
1944
|
+
);
|
|
1945
|
+
const ctrl = new OverlayController({
|
|
1946
|
+
...withLocalTestConfig(),
|
|
1947
|
+
handlesAccessibility: true,
|
|
1948
|
+
isTooltip: true,
|
|
1949
|
+
invokerNode,
|
|
1950
|
+
});
|
|
1951
|
+
expect(ctrl.__wrappingDialogNode?.getAttribute('tabindex')).to.equal('-1');
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1925
1954
|
describe('Teardown', () => {
|
|
1926
1955
|
it('restores [role] on dialog content', async () => {
|
|
1927
1956
|
const invokerNode = /** @type {HTMLElement} */ (
|
|
@@ -41,7 +41,7 @@ export interface OverlayConfig {
|
|
|
41
41
|
contentNode?: HTMLElement;
|
|
42
42
|
/** The wrapper element of contentNode, used to supply inline positioning styles. When a Popper arrow is needed, it acts as parent of the arrow node. Will be automatically created for global and non projected contentNodes. Required when used in shadow dom mode or when Popper arrow is supplied. Essential for allowing webcomponents to style their projected contentNodes */
|
|
43
43
|
contentWrapperNode?: HTMLElement;
|
|
44
|
-
/** The element that is placed
|
|
44
|
+
/** The element that is placed behind the contentNode. When not provided and `hasBackdrop` is true, a backdropNode will be automatically created */
|
|
45
45
|
backdropNode?: HTMLElement;
|
|
46
46
|
/** The element that should be called `.focus()` on after dialog closes */
|
|
47
47
|
elementToFocusAfterHide?: HTMLElement;
|
|
@@ -59,7 +59,7 @@ export interface OverlayConfig {
|
|
|
59
59
|
trapsKeyboardFocus?: boolean;
|
|
60
60
|
/** Hides the overlay when pressing [ esc ] */
|
|
61
61
|
hidesOnEsc?: boolean;
|
|
62
|
-
/** Hides the overlay when clicking next to it,
|
|
62
|
+
/** Hides the overlay when clicking next to it, excluding invoker */
|
|
63
63
|
hidesOnOutsideClick?: boolean;
|
|
64
64
|
/** Hides the overlay when pressing esc, even when contentNode has no focus */
|
|
65
65
|
hidesOnOutsideEsc?: boolean;
|
|
@@ -82,6 +82,8 @@ export interface OverlayConfig {
|
|
|
82
82
|
|
|
83
83
|
/** Has a totally different interaction- and accessibility pattern from all other overlays. Will behave as role="tooltip" element instead of a role="dialog" element */
|
|
84
84
|
isTooltip?: boolean;
|
|
85
|
+
/** The alertdialog role is to be used on modal alert dialogs that interrupt a user's workflow to communicate an important message and require a response. */
|
|
86
|
+
isAlertDialog?: boolean;
|
|
85
87
|
/** By default, the tooltip content is a 'description' for the invoker (uses aria-describedby) Setting this property to 'label' makes the content function as a label (via aria-labelledby) */
|
|
86
88
|
invokerRelation?: 'label' | 'description';
|
|
87
89
|
|