@abyss-project/commons-front-core 13.3.0 → 14.0.0

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.
Files changed (94) hide show
  1. package/catalog/captures-plan.ts +4 -1
  2. package/catalog/catalog-entries.ts +6 -1
  3. package/catalog/expected-error.ts +6 -0
  4. package/catalog/pages/CookieConsent.tsx +38 -0
  5. package/catalog/pages/ErrorBoundary.tsx +22 -0
  6. package/catalog/pages/PasswordField.tsx +62 -0
  7. package/catalog/pages/Toast.tsx +17 -6
  8. package/catalog/pages/index.ts +6 -0
  9. package/catalog/specs/captures.spec.ts +10 -0
  10. package/catalog/specs/catalog-page.ts +6 -1
  11. package/catalog/specs/write-capture-index.ts +1 -0
  12. package/dist/{AbyssCloud-D_AbU9k7.js → AbyssCloud-B3rMelRi.js} +636 -712
  13. package/dist/CookieConsentSettings-DyJGEnVA.js +505 -0
  14. package/dist/{use-rich-intl.hook-BETUyCkW.js → Surface-B_J_I_1i.js} +700 -147
  15. package/dist/Tabs-_TzugjHZ.js +1941 -0
  16. package/dist/auth/components/AuthHeroLayout/auth-hero-gradient.d.ts +18 -0
  17. package/dist/auth/components/shared/AuthLoadingScreen.component.d.ts +5 -0
  18. package/dist/auth/components/shared/use-async-resource.hook.d.ts +4 -1
  19. package/dist/auth/components/shared/use-async-resource.hook.spec.d.ts +1 -0
  20. package/dist/components/business/CookieConsent/AbyssCookieConsentProvider.spec.d.ts +1 -0
  21. package/dist/components/business/CookieConsent/CookieConsent.spec.d.ts +1 -0
  22. package/dist/{legacy/components/CookieConsent/CookieConsentBanner.component.d.ts → components/business/CookieConsent/CookieConsentBanner.d.ts} +1 -2
  23. package/dist/components/business/CookieConsent/CookieConsentSettings.d.ts +1 -0
  24. package/dist/{legacy/components → components/business}/CookieConsent/index.d.ts +2 -2
  25. package/dist/components/business/index.d.ts +3 -0
  26. package/dist/components/inputs/FormField/FormField.d.ts +5 -1
  27. package/dist/components/inputs/PasswordField/PasswordField.d.ts +40 -0
  28. package/dist/components/inputs/PasswordField/PasswordField.spec.d.ts +1 -0
  29. package/dist/components/inputs/index.d.ts +1 -0
  30. package/dist/components/layers/Toast/Toast.d.ts +3 -18
  31. package/dist/components/layers/Toast/toast-clearance.d.ts +6 -0
  32. package/dist/components/layers/Toast/toast-store.d.ts +76 -0
  33. package/dist/components/states/ErrorBoundary/ErrorBoundary.d.ts +17 -0
  34. package/dist/components/states/ErrorBoundary/ErrorBoundary.spec.d.ts +1 -0
  35. package/dist/components/states/PageState/PageState.d.ts +3 -1
  36. package/dist/components/states/index.d.ts +1 -0
  37. package/dist/cookie-consent.d.ts +1 -1
  38. package/dist/cookie-consent.js +14 -15
  39. package/dist/demo/DemoBanner/DemoBanner.d.ts +16 -0
  40. package/dist/demo/DemoBanner/DemoBanner.spec.d.ts +1 -0
  41. package/dist/demo/DemoBanner/index.d.ts +1 -0
  42. package/dist/demo/demo-blocked.interceptor.d.ts +7 -0
  43. package/dist/demo/demo.en.d.ts +17 -0
  44. package/dist/demo/demo.fr.d.ts +17 -0
  45. package/dist/demo/index.d.ts +3 -0
  46. package/dist/demo/tests/demo-blocked.interceptor.spec.d.ts +1 -0
  47. package/dist/demo/use-demo-account.hook.d.ts +19 -0
  48. package/dist/design-theme-D5L0JNbX.js +26599 -0
  49. package/dist/{editors-CUsZNFfw.js → editors-C6Rocm6M.js} +83 -83
  50. package/dist/editors.js +1 -1
  51. package/dist/hooks/use-rich-intl.hook.d.ts +80 -0
  52. package/dist/{index-BQ4GXlMJ.js → index-5AavH8Oo.js} +1 -1
  53. package/dist/{index-Bn4gTAno.js → index-B58_3EoW.js} +2 -2
  54. package/dist/{index-DuMzZrqc.js → index-BElPTqLa.js} +2 -2
  55. package/dist/{index-D1ro2oTw.js → index-BI4ooqu1.js} +2 -2
  56. package/dist/{index-TB7ekLl-.js → index-BYDMmzla.js} +2 -2
  57. package/dist/{index-CM0x-TnE.js → index-C1GCQ2Ke.js} +2 -2
  58. package/dist/{index-DEBTvCWD.js → index-C1I7MW-q.js} +3 -3
  59. package/dist/{index-pnfk6VZ7.js → index-CDI4_t7D.js} +4 -4
  60. package/dist/{index-Lyt4_W7a.js → index-CDWJlhuZ.js} +2 -2
  61. package/dist/{index-C7a2XXcz.js → index-CKJMsjnV.js} +4 -4
  62. package/dist/{index-CDpC4_po.js → index-Cskjr11h.js} +2 -2
  63. package/dist/{index-DErJ9c2q.js → index-CuYK_i3Q.js} +2 -2
  64. package/dist/{index-GTkZik4b.js → index-Cwgj3j50.js} +3 -3
  65. package/dist/{index-CjV5ui_B.js → index-Czhx2OUB.js} +2 -2
  66. package/dist/{index-CiDg4J7c.js → index-DGoR-pBV.js} +4 -4
  67. package/dist/{index-YzYX-H93.js → index-DjAZPCEQ.js} +3 -3
  68. package/dist/{index-Ba41aBBN.js → index-Dutd4cJm.js} +2 -2
  69. package/dist/{index-DobX4g2g.js → index-Dym_r-do.js} +2 -2
  70. package/dist/{index-BhFOCfCj.js → index-TshVV9T4.js} +2 -2
  71. package/dist/{index-BzoBMWdL.js → index-UjGNADrL.js} +3 -3
  72. package/dist/{index-CYUixRIB.js → index-u-EAOYOb.js} +2 -2
  73. package/dist/index.d.ts +1 -0
  74. package/dist/index.js +5483 -4750
  75. package/dist/legacy/components/Spotlight/ProjectLinkSelector/index.d.ts +1 -4
  76. package/dist/legacy/components/Support/AbyssServiceSelect.component.d.ts +1 -1
  77. package/dist/legacy/components/index.d.ts +1 -1
  78. package/dist/legacy.js +9626 -8129
  79. package/dist/translations/en.d.ts +39 -0
  80. package/dist/translations/fr.d.ts +39 -0
  81. package/dist/utils/index.d.ts +2 -0
  82. package/dist/utils/project-link-mode.d.ts +4 -0
  83. package/dist/utils/project-link-wording.utils.d.ts +1 -1
  84. package/dist/utils/service-config.d.ts +8 -0
  85. package/dist/utils/service-config.spec.d.ts +1 -0
  86. package/package.json +3 -3
  87. package/vite.config.ts +1 -1
  88. package/dist/AbyssCookieConsentProvider-Ds99df0E.js +0 -4562
  89. package/dist/CookieConsentSettings.component-DoWLHqw5.js +0 -78
  90. package/dist/Tabs-C1qcQ2i5.js +0 -2335
  91. package/dist/design-theme-CYGZ6mAh.js +0 -23975
  92. package/dist/legacy/components/CookieConsent/CookieConsentSettings.component.d.ts +0 -2
  93. /package/dist/{legacy/components/CookieConsent/AbyssCookieConsentProvider.spec.d.ts → auth/components/SecuritySettings/sections/SessionsSection.component.spec.d.ts} +0 -0
  94. /package/dist/{legacy/components → components/business}/CookieConsent/AbyssCookieConsentProvider.d.ts +0 -0
@@ -35,7 +35,8 @@ export type CaptureState =
35
35
  | 'list-open'
36
36
  | 'calendar-open'
37
37
  | 'panel-open'
38
- | 'menu-open';
38
+ | 'menu-open'
39
+ | 'settings-open';
39
40
 
40
41
  /** File name suffix of each state. */
41
42
  const STATE_SUFFIX: Record<CaptureState, string> = {
@@ -45,6 +46,7 @@ const STATE_SUFFIX: Record<CaptureState, string> = {
45
46
  'calendar-open': '-calendrier',
46
47
  'panel-open': '-ouvert',
47
48
  'menu-open': '-ouvert',
49
+ 'settings-open': '-reglages',
48
50
  };
49
51
 
50
52
  /** The states shown beyond arrival: what a closed control hides (options, calendar, sheet). */
@@ -56,6 +58,7 @@ const EXTRA_STATES: Record<string, CaptureState> = {
56
58
  NotificationsMenu: 'panel-open',
57
59
  AccountMenu: 'panel-open',
58
60
  ValueMenu: 'menu-open',
61
+ CookieConsent: 'settings-open',
59
62
  };
60
63
 
61
64
  export interface Capture {
@@ -65,6 +65,7 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
65
65
  entry('SwipeDeck', 'mobile', 'swipe-deck', RESPONSIVE),
66
66
  entry('PageState', 'states', 'page-state', { mobileCapture: true }),
67
67
  entry('Callout', 'states', 'callout', RESPONSIVE),
68
+ entry('ErrorBoundary', 'states', 'error-boundary', { mobileCapture: true }),
68
69
  entry('DataTable', 'data', 'data-table', RESPONSIVE),
69
70
  entry('KeyValueList', 'data', 'key-value-list'),
70
71
  entry('StatTile', 'data', 'stat-tile'),
@@ -91,6 +92,9 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
91
92
  mobileCapture: true,
92
93
  }),
93
94
  entry('QRCodeModal', 'business', 'qr-code-modal', { mobileCapture: true }),
95
+ entry('CookieConsent', 'business', 'cookie-consent', {
96
+ mobileCapture: true,
97
+ }),
94
98
  entry('Modal', 'layers', 'modal', { mobileCapture: true }),
95
99
  entry('ConfirmDialog', 'layers', 'confirm-dialog'),
96
100
  entry('ConfirmDialogWarning', 'layers', 'confirm-dialog-warning'),
@@ -98,7 +102,7 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
98
102
  entry('PreviewDrawer', 'layers', 'preview-drawer', { mobileCapture: true }),
99
103
  entry('Menu', 'layers', 'menu'),
100
104
  entry('AnchoredPanel', 'layers', 'anchored-panel', { mobileCapture: true }),
101
- entry('Toast', 'layers', 'toast'),
105
+ entry('Toast', 'layers', 'toast', { mobileCapture: true }),
102
106
  entry('CommandPalette', 'layers', 'command-palette'),
103
107
  entry('ProductTour', 'layers', 'product-tour', { mobileCapture: true }),
104
108
  entry('RichSelect', 'inputs', 'rich-select'),
@@ -107,6 +111,7 @@ export const CATALOG_ENTRIES: CatalogEntry[] = [
107
111
  entry('ColorField', 'inputs', 'color-field'),
108
112
  entry('SegmentedControl', 'inputs', 'segmented-control'),
109
113
  entry('FormField', 'inputs', 'form-field'),
114
+ entry('PasswordField', 'inputs', 'password-field', RESPONSIVE),
110
115
  entry('NumberField', 'inputs', 'number-field'),
111
116
  entry('SettingsRow', 'inputs', 'settings-row', RESPONSIVE),
112
117
  entry('CopyField', 'inputs', 'copy-field', RESPONSIVE),
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Marker carried by an error a catalog page throws on purpose (the error screen's own page): React
3
+ * in development reports an error its boundary caught as a page error too, and the captures spec
4
+ * ignores only the errors that carry it.
5
+ */
6
+ export const EXPECTED_CATALOG_ERROR = '[catalog: expected]';
@@ -0,0 +1,38 @@
1
+ import Button from '@mui/material/Button';
2
+ import { CookieConsentBanner } from '../../src/components/business/CookieConsent/CookieConsentBanner';
3
+ import type { UseCookieConsent } from '../../src/hooks/use-cookie-consent.hook';
4
+ import { Example, LastEvent, Showcase, useEventLog } from '../frame';
5
+
6
+ const yesNo = (value: boolean): string => (value ? 'oui' : 'non');
7
+
8
+ export const CookieConsentPage = () => {
9
+ const { log } = useEventLog();
10
+ const api: UseCookieConsent = {
11
+ consent: null,
12
+ isResolved: true,
13
+ acceptAll: () => log('tout accepter'),
14
+ rejectAll: () => log('tout refuser'),
15
+ save: (choices) =>
16
+ log(
17
+ `enregistrer · audience ${yesNo(choices.analytics)}, marketing ${yesNo(
18
+ choices.marketing,
19
+ )}`,
20
+ ),
21
+ reset: () => log('réinitialiser'),
22
+ };
23
+ return (
24
+ <Showcase
25
+ family="Métier"
26
+ name="CookieConsent"
27
+ description="Bandeau de consentement en bas à droite (pleine largeur sous 768 px) : accepter et refuser pèsent autant ; « Personnaliser » ouvre la fenêtre de réglages, et le bandeau s'efface tant qu'elle est ouverte."
28
+ >
29
+ <Example title="Bandeau et fenêtre de réglages">
30
+ <Button variant="outlined" disabled>
31
+ Le bandeau est posé sur la page
32
+ </Button>
33
+ </Example>
34
+ <LastEvent />
35
+ <CookieConsentBanner api={api} />
36
+ </Showcase>
37
+ );
38
+ };
@@ -0,0 +1,22 @@
1
+ import { EXPECTED_CATALOG_ERROR } from '../expected-error';
2
+ import { ErrorBoundary } from '../../src/components/states/ErrorBoundary/ErrorBoundary';
3
+
4
+ /**
5
+ * A child whose render fails, so the boundary shows its screen on arrival. The marker lets the
6
+ * captures spec tell this deliberate error from a real one (it stays in the folded detail).
7
+ */
8
+ const Broken = (): never => {
9
+ throw new Error(
10
+ `Impossible de lire « account.plan » : la valeur est absente. ${EXPECTED_CATALOG_ERROR}`,
11
+ );
12
+ };
13
+
14
+ /**
15
+ * The root error screen at full frame (`fullHeight`), as the apps mount it above their router: it
16
+ * is the page itself, with no catalog frame around it.
17
+ */
18
+ export const ErrorBoundaryPage = () => (
19
+ <ErrorBoundary appName="abyss-main" fullHeight>
20
+ <Broken />
21
+ </ErrorBoundary>
22
+ );
@@ -0,0 +1,62 @@
1
+ import { useState } from 'react';
2
+ import type { ChangeEvent } from 'react';
3
+ import { PasswordField } from '../../src/components/inputs/PasswordField/PasswordField';
4
+ import { Columns, Example, Showcase } from '../frame';
5
+
6
+ export const PasswordFieldPage = () => {
7
+ const [current, setCurrent] = useState('correct horse');
8
+ const [next, setNext] = useState('');
9
+ const [confirmation, setConfirmation] = useState('abc');
10
+ const bind =
11
+ (set: (value: string) => void) => (event: ChangeEvent<HTMLInputElement>) =>
12
+ set(event.target.value);
13
+ return (
14
+ <Showcase
15
+ family="Saisie"
16
+ name="PasswordField"
17
+ description="Champ mot de passe sur FormField : libellé, aide et erreur comme les autres champs, et un bouton de 44 px, accessible au clavier, qui affiche ou masque la saisie."
18
+ >
19
+ <Example title="Saisie masquée, aide, erreur">
20
+ <Columns>
21
+ <PasswordField
22
+ label="Mot de passe actuel"
23
+ name="current"
24
+ autoComplete="current-password"
25
+ required
26
+ value={current}
27
+ onChange={bind(setCurrent)}
28
+ />
29
+ <PasswordField
30
+ label="Nouveau mot de passe"
31
+ name="next"
32
+ autoComplete="new-password"
33
+ help="Au moins 10 caractères. Une phrase est plus sûre qu'un mot compliqué."
34
+ required
35
+ value={next}
36
+ onChange={bind(setNext)}
37
+ />
38
+ <PasswordField
39
+ label="Confirmer le nouveau mot de passe"
40
+ name="confirmation"
41
+ autoComplete="new-password"
42
+ error="Les deux mots de passe ne correspondent pas."
43
+ required
44
+ value={confirmation}
45
+ onChange={bind(setConfirmation)}
46
+ />
47
+ </Columns>
48
+ </Example>
49
+ <Example title="Désactivé">
50
+ <Columns>
51
+ <PasswordField
52
+ label="Mot de passe"
53
+ name="locked"
54
+ disabled
55
+ value="verrouillé"
56
+ onChange={() => undefined}
57
+ />
58
+ </Columns>
59
+ </Example>
60
+ </Showcase>
61
+ );
62
+ };
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef } from 'react';
2
2
  import Button from '@mui/material/Button';
3
3
  import {
4
4
  ToastProvider,
5
+ toast,
5
6
  useToast,
6
7
  ToastTone,
7
8
  } from '../../src/components/layers/Toast/Toast';
@@ -16,8 +17,14 @@ const TONE_LABELS: Record<ToastTone, string> = {
16
17
  [ToastTone.Danger]: 'erreur',
17
18
  };
18
19
 
20
+ /** A progress notification, shown at 40 %: the page shows it next to the four tones. */
21
+ const showProgress = () => {
22
+ const id = toast.progress('Envoi de 12 fichiers…');
23
+ toast.update(id, { progress: 0.4 });
24
+ };
25
+
19
26
  const ToastDemo = () => {
20
- const toast = useToast();
27
+ const toasts = useToast();
21
28
  const { log } = useEventLog();
22
29
  const shown = useRef(false);
23
30
 
@@ -58,8 +65,9 @@ const ToastDemo = () => {
58
65
  return;
59
66
  }
60
67
  shown.current = true;
61
- samples.forEach((sample) => toast.show(sample));
62
- }, [samples, toast]);
68
+ samples.forEach((sample) => toasts.show(sample));
69
+ showProgress();
70
+ }, [samples, toasts]);
63
71
 
64
72
  return (
65
73
  <Row>
@@ -67,11 +75,14 @@ const ToastDemo = () => {
67
75
  <Button
68
76
  key={sample.tone}
69
77
  variant="outlined"
70
- onClick={() => toast.show(sample)}
78
+ onClick={() => toasts.show(sample)}
71
79
  >
72
80
  Notifier · {TONE_LABELS[sample.tone]}
73
81
  </Button>
74
82
  ))}
83
+ <Button variant="outlined" onClick={showProgress}>
84
+ Notifier · progression
85
+ </Button>
75
86
  </Row>
76
87
  );
77
88
  };
@@ -81,9 +92,9 @@ export const ToastPage = () => (
81
92
  <Showcase
82
93
  family="Couches"
83
94
  name="Toast"
84
- description="Notification passagère en bas à droite : quatre tons, une action facultative, retenue au survol ou au focus."
95
+ description="Notification passagère en bas à droite, au-dessus du bandeau de cookies quand il est là : quatre tons, une action facultative, retenue au survol ou au focus, et une notification de progression."
85
96
  >
86
- <Example title="Les quatre tons, deux avec action">
97
+ <Example title="Les quatre tons, deux avec action, et une progression">
87
98
  <ToastDemo />
88
99
  </Example>
89
100
  <LastEvent />
@@ -32,6 +32,9 @@ import { ExpressionSearchPage } from './ExpressionSearch';
32
32
  import { FacetPanelPage } from './FacetPanel';
33
33
  import { FilterBarPage } from './FilterBar';
34
34
  import { FormFieldPage } from './FormField';
35
+ import { PasswordFieldPage } from './PasswordField';
36
+ import { ErrorBoundaryPage } from './ErrorBoundary';
37
+ import { CookieConsentPage } from './CookieConsent';
35
38
  import { ImportButtonPage } from './ImportButton';
36
39
  import { ImportFromCloudModalPage } from './ImportFromCloudModal';
37
40
  import { InfectedDownloadModalPage } from './InfectedDownloadModal';
@@ -106,6 +109,9 @@ export const CATALOG_PAGES: Record<string, ComponentType> = {
106
109
  FacetPanel: FacetPanelPage,
107
110
  FilterBar: FilterBarPage,
108
111
  FormField: FormFieldPage,
112
+ PasswordField: PasswordFieldPage,
113
+ ErrorBoundary: ErrorBoundaryPage,
114
+ CookieConsent: CookieConsentPage,
109
115
  ImportButton: ImportButtonPage,
110
116
  ImportFromCloudModal: ImportFromCloudModalPage,
111
117
  InfectedDownloadModal: InfectedDownloadModalPage,
@@ -39,6 +39,16 @@ const REACH: Record<
39
39
  await expect(menu).toBeVisible();
40
40
  await expect(menu.getByRole('menuitem')).toHaveCount(6);
41
41
  },
42
+ // The cookie banner's settings: the banner leaves while the sheet is open.
43
+ 'settings-open': async (page) => {
44
+ await page.getByRole('button', { name: 'Personnaliser' }).click();
45
+ await expect(
46
+ page.getByRole('dialog', { name: 'Personnaliser mes cookies' }),
47
+ ).toBeVisible();
48
+ await expect(
49
+ page.getByRole('dialog', { name: 'Vos préférences de cookies' }),
50
+ ).toHaveCount(0);
51
+ },
42
52
  // The first top bar trigger of the page: the notifications bell or the account avatar.
43
53
  'panel-open': async (page) => {
44
54
  await page.locator('[aria-haspopup]').first().click();
@@ -1,6 +1,7 @@
1
1
  import type { Page } from '@playwright/test';
2
2
  import { catalogPath } from '../catalog-entries';
3
3
  import type { CatalogTheme } from '../catalog-entries';
4
+ import { EXPECTED_CATALOG_ERROR } from '../expected-error';
4
5
 
5
6
  /** Longest enter transition of the design system (`durations.slow`, 260 ms), with margin. */
6
7
  const SETTLE_MS = 400;
@@ -16,7 +17,11 @@ export const openCatalogPage = async (
16
17
  viewport: { width: number; height: number },
17
18
  ): Promise<void> => {
18
19
  const errors: string[] = [];
19
- page.on('pageerror', (error) => errors.push(error.message));
20
+ page.on('pageerror', (error) => {
21
+ if (!error.message.includes(EXPECTED_CATALOG_ERROR)) {
22
+ errors.push(error.message);
23
+ }
24
+ });
20
25
  await page.setViewportSize(viewport);
21
26
  await page.goto(catalogPath(name, theme));
22
27
  await page.waitForLoadState('networkidle');
@@ -26,6 +26,7 @@ const STATE_LABEL: Record<CaptureState, string> = {
26
26
  'calendar-open': ', calendrier ouvert',
27
27
  'panel-open': ', panneau ouvert',
28
28
  'menu-open': ', menu ouvert',
29
+ 'settings-open': ', réglages ouverts',
29
30
  };
30
31
 
31
32
  const describe = (capture: Capture): string =>