@byline/admin 5.1.3 → 5.2.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 (93) hide show
  1. package/dist/modules/analytics/components/dashboard.d.ts +13 -3
  2. package/dist/modules/analytics/components/dashboard.js +86 -68
  3. package/dist/modules/analytics/components/dashboard.module.js +10 -1
  4. package/dist/modules/analytics/components/dashboard_module.css +57 -0
  5. package/dist/modules/analytics/components/ranked-list-modal.d.ts +32 -0
  6. package/dist/modules/analytics/components/ranked-list-modal.js +145 -0
  7. package/dist/modules/analytics/components/ranking.d.ts +45 -0
  8. package/dist/modules/analytics/components/ranking.js +71 -0
  9. package/dist/modules/analytics/components/ranking.test.node.d.ts +8 -0
  10. package/package.json +14 -14
  11. package/src/modules/analytics/components/dashboard.module.css +69 -0
  12. package/src/modules/analytics/components/dashboard.tsx +105 -83
  13. package/src/modules/analytics/components/ranked-list-modal.test.tsx +154 -0
  14. package/src/modules/analytics/components/ranked-list-modal.tsx +159 -0
  15. package/src/modules/analytics/components/ranking.test.node.ts +43 -0
  16. package/src/modules/analytics/components/ranking.tsx +122 -0
  17. package/dist/exports-parity.test.node.js +0 -37
  18. package/dist/fields/array/array-field.jsx +0 -209
  19. package/dist/fields/blocks/blocks-field.jsx +0 -248
  20. package/dist/fields/checkbox/checkbox-field.jsx +0 -24
  21. package/dist/fields/code/code-editor.jsx +0 -211
  22. package/dist/fields/code/code-field.jsx +0 -109
  23. package/dist/fields/column-formatter.jsx +0 -25
  24. package/dist/fields/date-time-formatter.jsx +0 -18
  25. package/dist/fields/datetime/datetime-field.jsx +0 -22
  26. package/dist/fields/draggable-context-menu.jsx +0 -50
  27. package/dist/fields/field-admin.test.node.js +0 -40
  28. package/dist/fields/field-renderer.jsx +0 -136
  29. package/dist/fields/field-services-context.jsx +0 -17
  30. package/dist/fields/file/file-field.jsx +0 -176
  31. package/dist/fields/file/file-upload-field.jsx +0 -89
  32. package/dist/fields/group/group-field.jsx +0 -46
  33. package/dist/fields/image/image-field.jsx +0 -183
  34. package/dist/fields/image/image-upload-field.jsx +0 -157
  35. package/dist/fields/local-date-time.jsx +0 -69
  36. package/dist/fields/locale-badge.jsx +0 -19
  37. package/dist/fields/numerical/numerical-field.jsx +0 -93
  38. package/dist/fields/relation/relation-column-formatter.jsx +0 -74
  39. package/dist/fields/relation/relation-display.jsx +0 -118
  40. package/dist/fields/relation/relation-field.jsx +0 -108
  41. package/dist/fields/relation/relation-many-field.jsx +0 -142
  42. package/dist/fields/relation/relation-picker.jsx +0 -240
  43. package/dist/fields/relation/relation-summary.jsx +0 -45
  44. package/dist/fields/select/select-field.jsx +0 -27
  45. package/dist/fields/sortable-item.jsx +0 -54
  46. package/dist/fields/text/text-field.jsx +0 -79
  47. package/dist/fields/text-area/text-area-field.jsx +0 -79
  48. package/dist/forms/available-locales-reconcile.test.node.js +0 -43
  49. package/dist/forms/available-locales-widget.jsx +0 -63
  50. package/dist/forms/document-actions.jsx +0 -486
  51. package/dist/forms/form-context.jsx +0 -598
  52. package/dist/forms/form-modals.jsx +0 -128
  53. package/dist/forms/form-renderer.jsx +0 -532
  54. package/dist/forms/form-status-display.jsx +0 -69
  55. package/dist/forms/navigation-guard.jsx +0 -68
  56. package/dist/forms/nested-path.test.node.js +0 -115
  57. package/dist/forms/path-widget.jsx +0 -125
  58. package/dist/forms/pending-uploads.test.node.js +0 -19
  59. package/dist/forms/repeating-items.test.node.js +0 -29
  60. package/dist/forms/scheduled-publication-control.jsx +0 -389
  61. package/dist/forms/scheduled-publication-state.test.node.js +0 -121
  62. package/dist/forms/scheduled-publication-time.test.node.js +0 -70
  63. package/dist/forms/status-transitions.test.node.js +0 -76
  64. package/dist/forms/tree-placement-widget.jsx +0 -160
  65. package/dist/forms/upload-executor.test.node.js +0 -368
  66. package/dist/forms/use-form-layout.test.node.js +0 -69
  67. package/dist/modules/admin-account/components/change-password.jsx +0 -169
  68. package/dist/modules/admin-account/components/container.jsx +0 -164
  69. package/dist/modules/admin-account/components/preferences.jsx +0 -148
  70. package/dist/modules/admin-account/components/theme-switch.jsx +0 -52
  71. package/dist/modules/admin-account/components/update.jsx +0 -186
  72. package/dist/modules/admin-permissions/components/inspector.jsx +0 -200
  73. package/dist/modules/admin-preferences/schemas.test.node.js +0 -48
  74. package/dist/modules/admin-roles/components/create.jsx +0 -155
  75. package/dist/modules/admin-roles/components/permissions.jsx +0 -239
  76. package/dist/modules/admin-roles/components/update.jsx +0 -148
  77. package/dist/modules/admin-users/components/create.jsx +0 -206
  78. package/dist/modules/admin-users/components/roles.jsx +0 -126
  79. package/dist/modules/admin-users/components/set-password.jsx +0 -133
  80. package/dist/modules/admin-users/components/update.jsx +0 -215
  81. package/dist/modules/analytics/components/dashboard.jsx +0 -208
  82. package/dist/modules/analytics/components/dashboard.test.node.js +0 -162
  83. package/dist/modules/analytics/components/timeseries.jsx +0 -193
  84. package/dist/modules/auth/components/sign-in-form.jsx +0 -89
  85. package/dist/modules/auth/safe-redirect.test.node.js +0 -40
  86. package/dist/modules/auth/sign-in-form-props.test.node.js +0 -7
  87. package/dist/presentation/group.jsx +0 -26
  88. package/dist/presentation/row.jsx +0 -23
  89. package/dist/presentation/tabs.jsx +0 -39
  90. package/dist/services/admin-services-context.jsx +0 -17
  91. package/dist/widgets/diff-viewer/diff-modal.jsx +0 -110
  92. package/dist/widgets/source-locale-badge/source-locale-badge.jsx +0 -28
  93. package/dist/widgets/status-badge/status-badge.jsx +0 -46
@@ -1,27 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { ErrorText, Label, Select } from '@byline/ui/react';
9
- import cx from 'clsx';
10
- import { useFieldError, useFieldValue, useIsDirty } from '../../forms/form-context';
11
- import styles from './select-field.module.css';
12
- export const SelectField = ({ field, value, defaultValue, onChange, id, path, }) => {
13
- const fieldPath = path ?? field.name;
14
- const fieldError = useFieldError(fieldPath);
15
- const isDirty = useIsDirty(fieldPath);
16
- const fieldValue = useFieldValue(fieldPath);
17
- const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
18
- const htmlId = id ?? fieldPath;
19
- return (<div className={`byline-field-select ${field.name}`}>
20
- {field.label && (<Label id={htmlId} htmlFor={htmlId} label={field.label} required={!field.optional} className={cx('byline-field-select-label', styles.label)}/>)}
21
- <Select size="xs" variant="outlined" id={htmlId} name={field.name} placeholder="Select an option" required={!field.optional} value={incomingValue} ariaLabel={field.label} helpText={field.helpText} items={field.options.map((opt) => ({ value: opt.value, label: opt.label }))} onValueChange={(value) => {
22
- if (value != null)
23
- onChange?.(value);
24
- }} className={cx(isDirty && ['byline-field-select-dirty', styles.dirty])}/>
25
- {fieldError && <ErrorText id={`${field.name}-error`} text={fieldError}/>}
26
- </div>);
27
- };
@@ -1,54 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { useState } from 'react';
9
- import { useTranslation } from '@byline/i18n/react';
10
- import { ChevronDownIcon, GripperVerticalIcon, useSortable } from '@byline/ui/react';
11
- import cx from 'clsx';
12
- import { DraggableContextMenu } from './draggable-context-menu';
13
- import styles from './sortable-item.module.css';
14
- const ItemFrame = ({ label, children, onAddBelow, onRemove, grip, rootRef, style, dragging = false, rootClassName, }) => {
15
- const { t } = useTranslation('byline-admin');
16
- const [collapsed, setCollapsed] = useState(false);
17
- return (<div ref={rootRef} style={style} className={cx('byline-sortable', styles.root, rootClassName, dragging && ['byline-sortable-dragging', styles.dragging], collapsed && ['byline-sortable-collapsed', styles.collapsed])}>
18
- <div className={cx('byline-sortable-header', styles.header, !collapsed && ['byline-sortable-header-expanded', styles['header-expanded']])}>
19
- {grip}
20
- <div className={cx('byline-sortable-label', styles.label)}>{label}</div>
21
- <DraggableContextMenu onAddBelow={onAddBelow} onRemove={onRemove}/>
22
- <button type="button" className={cx('byline-sortable-toggle', styles.toggle)} onClick={() => setCollapsed((prev) => !prev)} aria-label={collapsed
23
- ? t('fields.sortable.expandAriaLabel')
24
- : t('fields.sortable.collapseAriaLabel')}>
25
- <ChevronDownIcon className={cx('byline-sortable-toggle-icon', styles['toggle-icon'], collapsed && ['byline-sortable-toggle-icon-rotated', styles['toggle-icon-rotated']])}/>
26
- </button>
27
- </div>
28
- <div className={cx('byline-sortable-content', styles.content, collapsed && ['byline-sortable-content-hidden', styles['content-hidden']])}>
29
- {children}
30
- </div>
31
- </div>);
32
- };
33
- export const SortableItem = ({ id, label, children, onAddBelow, onRemove, }) => {
34
- const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
35
- id,
36
- transition: {
37
- duration: 250,
38
- easing: 'cubic-bezier(0, 0.2, 0.2, 1)',
39
- },
40
- });
41
- const style = {
42
- transform: transform ? `translate3d(${transform.x}px, ${transform.y}px, 0)` : undefined,
43
- transition,
44
- zIndex: isDragging ? 10 : 'auto',
45
- };
46
- return (<ItemFrame label={label} onAddBelow={onAddBelow} onRemove={onRemove} rootRef={setNodeRef} style={style} dragging={isDragging} grip={<button type="button" className={cx('byline-sortable-grip', styles.grip)} {...attributes} {...listeners}>
47
- <GripperVerticalIcon className={cx('byline-sortable-grip-icon', styles['grip-icon'])}/>
48
- </button>}>
49
- {children}
50
- </ItemFrame>);
51
- };
52
- export const StaticItem = ({ label, children, onAddBelow, onRemove, }) => (<ItemFrame label={label} onAddBelow={onAddBelow} onRemove={onRemove} rootClassName="byline-sortable-static">
53
- {children}
54
- </ItemFrame>);
@@ -1,79 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { useCallback } from 'react';
9
- import { Input, Label } from '@byline/ui/react';
10
- import cx from 'clsx';
11
- import { useFieldError, useFieldValue } from '../../forms/form-context';
12
- import { LocaleBadge } from '../locale-badge';
13
- import styles from './text-field.module.css';
14
- export const TextField = ({ field, value, defaultValue, onChange, id, path, locale, components, }) => {
15
- const fieldPath = path ?? field.name;
16
- const fieldError = useFieldError(fieldPath);
17
- const fieldValue = useFieldValue(fieldPath);
18
- const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
19
- const htmlId = id ?? fieldPath;
20
- const handleChange = useCallback((value) => {
21
- if (onChange) {
22
- onChange(value);
23
- }
24
- }, [onChange]);
25
- // Custom component slots (from admin config)
26
- const slots = components;
27
- const CustomLabel = slots?.Label;
28
- const CustomHelpText = slots?.HelpText;
29
- const CustomField = slots?.Field;
30
- const BeforeField = slots?.beforeField;
31
- const AfterField = slots?.afterField;
32
- // Shared props available to every slot component
33
- const slotBaseProps = {
34
- field: field,
35
- path: fieldPath,
36
- value: incomingValue,
37
- error: fieldError,
38
- id: htmlId,
39
- };
40
- // When a locale is active, render a custom Label+badge and suppress the
41
- // Input's own label so the locale indicator appears in the label row.
42
- const showBadge = !!locale && !!field.label;
43
- // Determine whether the label is handled externally (by a custom slot or
44
- // the locale badge row) so Input doesn't render its own.
45
- const hasCustomLabel = !!CustomLabel;
46
- const suppressInputLabel = showBadge || hasCustomLabel;
47
- const suppressInputHelpText = !!CustomHelpText;
48
- const labelRowClass = cx('byline-field-text-label-row', styles['label-row']);
49
- // ── Label rendering ──────────────────────────────────────────
50
- const renderLabel = () => {
51
- if (hasCustomLabel) {
52
- return (<div className={labelRowClass}>
53
- <CustomLabel {...slotBaseProps} label={field.label} required={!field.optional}/>
54
- {showBadge && <LocaleBadge locale={locale}/>}
55
- </div>);
56
- }
57
- if (showBadge) {
58
- return (<div className={labelRowClass}>
59
- <Label id={`${htmlId}-label`} htmlFor={htmlId} label={field.label} required={!field.optional}/>
60
- <LocaleBadge locale={locale}/>
61
- </div>);
62
- }
63
- return null;
64
- };
65
- // ── Field input rendering ────────────────────────────────────
66
- const renderInput = () => {
67
- if (CustomField) {
68
- return (<CustomField {...slotBaseProps} onChange={handleChange} defaultValue={defaultValue} placeholder={field.placeholder}/>);
69
- }
70
- return (<Input id={htmlId} name={field.name} label={suppressInputLabel ? undefined : field.label} required={!field.optional} helpText={suppressInputHelpText ? undefined : field.helpText} value={incomingValue} onChange={(e) => handleChange(e.target.value)} error={fieldError != null} errorText={fieldError}/>);
71
- };
72
- return (<div className={`byline-field-text ${field.name}`}>
73
- {renderLabel()}
74
- {BeforeField && <BeforeField {...slotBaseProps}/>}
75
- {renderInput()}
76
- {AfterField && <AfterField {...slotBaseProps}/>}
77
- {CustomHelpText && <CustomHelpText {...slotBaseProps} helpText={field.helpText}/>}
78
- </div>);
79
- };
@@ -1,79 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { useCallback } from 'react';
9
- import { Label, TextArea } from '@byline/ui/react';
10
- import cx from 'clsx';
11
- import { useFieldError, useFieldValue } from '../../forms/form-context';
12
- import { LocaleBadge } from '../locale-badge';
13
- import styles from './text-area-field.module.css';
14
- export const TextAreaField = ({ field, value, defaultValue, onChange, id, path, locale, components, }) => {
15
- const fieldPath = path ?? field.name;
16
- const fieldError = useFieldError(fieldPath);
17
- const fieldValue = useFieldValue(fieldPath);
18
- const incomingValue = value ?? fieldValue ?? defaultValue ?? '';
19
- const htmlId = id ?? fieldPath;
20
- const handleChange = useCallback((value) => {
21
- if (onChange) {
22
- onChange(value);
23
- }
24
- }, [onChange]);
25
- // Custom component slots (from admin config)
26
- const slots = components;
27
- const CustomLabel = slots?.Label;
28
- const CustomHelpText = slots?.HelpText;
29
- const CustomField = slots?.Field;
30
- const BeforeField = slots?.beforeField;
31
- const AfterField = slots?.afterField;
32
- // Shared props available to every slot component
33
- const slotBaseProps = {
34
- field: field,
35
- path: fieldPath,
36
- value: incomingValue,
37
- error: fieldError,
38
- id: htmlId,
39
- };
40
- // When a locale is active, render a custom Label+badge and suppress the
41
- // TextArea's own label so the locale indicator appears in the label row.
42
- const showBadge = !!locale && !!field.label;
43
- // Determine whether the label is handled externally (by a custom slot or
44
- // the locale badge row) so TextArea doesn't render its own.
45
- const hasCustomLabel = !!CustomLabel;
46
- const suppressInputLabel = showBadge || hasCustomLabel;
47
- const suppressInputHelpText = !!CustomHelpText;
48
- const labelRowClass = cx('byline-field-text-area-label-row', styles['label-row']);
49
- // ── Label rendering ──────────────────────────────────────────
50
- const renderLabel = () => {
51
- if (hasCustomLabel) {
52
- return (<div className={labelRowClass}>
53
- <CustomLabel {...slotBaseProps} label={field.label} required={!field.optional}/>
54
- {showBadge && <LocaleBadge locale={locale}/>}
55
- </div>);
56
- }
57
- if (showBadge) {
58
- return (<div className={labelRowClass}>
59
- <Label id={`${htmlId}-label`} htmlFor={htmlId} label={field.label} required={!field.optional}/>
60
- <LocaleBadge locale={locale}/>
61
- </div>);
62
- }
63
- return null;
64
- };
65
- // ── Field input rendering ────────────────────────────────────
66
- const renderInput = () => {
67
- if (CustomField) {
68
- return (<CustomField {...slotBaseProps} onChange={handleChange} defaultValue={defaultValue} placeholder={field.placeholder}/>);
69
- }
70
- return (<TextArea id={htmlId} name={field.name} label={suppressInputLabel ? undefined : field.label} required={!field.optional} helpText={suppressInputHelpText ? undefined : field.helpText} value={incomingValue} onChange={(e) => handleChange(e.target.value)} error={fieldError != null} errorText={fieldError} rows={4}/>);
71
- };
72
- return (<div className={`byline-field-text-area ${field.name}`}>
73
- {renderLabel()}
74
- {BeforeField && <BeforeField {...slotBaseProps}/>}
75
- {renderInput()}
76
- {AfterField && <AfterField {...slotBaseProps}/>}
77
- {CustomHelpText && <CustomHelpText {...slotBaseProps} helpText={field.helpText}/>}
78
- </div>);
79
- };
@@ -1,43 +0,0 @@
1
- /**
2
- * This Source Code is subject to the terms of the Mozilla Public
3
- * License, v. 2.0. If a copy of the MPL was not distributed with this
4
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
- *
6
- * Copyright (c) Infonomic Company Limited
7
- */
8
- import { describe, expect, it } from 'vitest';
9
- import { reconcileLocaleState } from './available-locales-reconcile.js';
10
- /**
11
- * The widget's reconciliation is expressed purely as Checkbox intent +
12
- * disabled state — no per-row text. `reconcileLocaleState` is the pure heart
13
- * of that mapping; the four cells below are the full truth table from
14
- * docs/08-internationalization/index.md.
15
- */
16
- describe('reconcileLocaleState', () => {
17
- it('ledger-complete + advertised → green, enabled (advertised & complete)', () => {
18
- expect(reconcileLocaleState(true, true)).toEqual({ intent: 'success', disabled: false });
19
- });
20
- it('ledger-complete + not advertised → green, enabled (invitation to advertise)', () => {
21
- expect(reconcileLocaleState(false, true)).toEqual({ intent: 'success', disabled: false });
22
- });
23
- it('not complete + advertised → amber, enabled (advertising an incomplete locale)', () => {
24
- // The ⚠ row: surfaced as `warning` and kept enabled so the editor can
25
- // uncheck to resolve the over-advertisement.
26
- expect(reconcileLocaleState(true, false)).toEqual({ intent: 'warning', disabled: false });
27
- });
28
- it('not complete + not advertised → gray, disabled (nothing to advertise)', () => {
29
- expect(reconcileLocaleState(false, false)).toEqual({ intent: 'noeffect', disabled: true });
30
- });
31
- it('completeness drives green regardless of advertised state', () => {
32
- // The ledger fact wins the colour when complete; only the disabled/amber
33
- // distinction depends on the advertised flag.
34
- expect(reconcileLocaleState(true, true).intent).toBe('success');
35
- expect(reconcileLocaleState(false, true).intent).toBe('success');
36
- });
37
- it('only the not-complete + not-advertised cell is non-interactive', () => {
38
- expect(reconcileLocaleState(true, true).disabled).toBe(false);
39
- expect(reconcileLocaleState(false, true).disabled).toBe(false);
40
- expect(reconcileLocaleState(true, false).disabled).toBe(false);
41
- expect(reconcileLocaleState(false, false).disabled).toBe(true);
42
- });
43
- });
@@ -1,63 +0,0 @@
1
- 'use client';
2
- /**
3
- * This Source Code is subject to the terms of the Mozilla Public
4
- * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
- *
7
- * Copyright (c) Infonomic Company Limited
8
- */
9
- import { useCallback, useMemo } from 'react';
10
- import { useTranslation } from '@byline/i18n/react';
11
- import { Checkbox, Label } from '@byline/ui/react';
12
- import cx from 'clsx';
13
- import { reconcileLocaleState } from './available-locales-reconcile';
14
- import styles from './available-locales-widget.module.css';
15
- import { useFormContext, useSystemAvailableLocales } from './form-context';
16
- export { reconcileLocaleState } from './available-locales-reconcile';
17
- /**
18
- * System-managed `availableLocales` widget — the editorial "advertise these
19
- * content locales" control. Renders one checkbox per content locale in the
20
- * sidebar, directly below the path widget. The checked state reflects the
21
- * stored advertised set (`useSystemAvailableLocales`); each row's intent
22
- * reconciles that against the structural ledger fact (`availableVersionLocales`)
23
- * via {@link reconcileLocaleState}. Opt-in: nothing is advertised until the
24
- * editor checks a (green) locale.
25
- *
26
- * Stable override handles: `.byline-form-available-locales`,
27
- * `.byline-form-available-locales-list`.
28
- */
29
- export const AvailableLocalesWidget = ({ disabled: mutationsBlocked = false, contentLocales, availableVersionLocales, }) => {
30
- const { t } = useTranslation('byline-admin');
31
- const { setSystemAvailableLocales } = useFormContext();
32
- const advertised = useSystemAvailableLocales();
33
- const advertisedSet = useMemo(() => new Set(advertised), [advertised]);
34
- const ledgerSet = useMemo(() => new Set(availableVersionLocales), [availableVersionLocales]);
35
- const toggle = useCallback((code, checked) => {
36
- if (mutationsBlocked)
37
- return;
38
- const next = new Set(advertised);
39
- if (checked) {
40
- next.add(code);
41
- }
42
- else {
43
- next.delete(code);
44
- }
45
- setSystemAvailableLocales([...next]);
46
- }, [mutationsBlocked, advertised, setSystemAvailableLocales]);
47
- if (contentLocales.length === 0) {
48
- return null;
49
- }
50
- return (<div className={cx('byline-form-available-locales', styles.container)}>
51
- <Label id="available-locales" htmlFor="available-locales-group" label={t('availableLocalesWidget.label')}/>
52
- <div id="available-locales-group" className={cx('byline-form-available-locales-list', styles.list)} role="group" aria-labelledby="label-for-available-locales" aria-describedby="available-locales-description">
53
- {contentLocales.map(({ code, label }) => {
54
- const checked = advertisedSet.has(code);
55
- const { intent, disabled } = reconcileLocaleState(checked, ledgerSet.has(code));
56
- return (<Checkbox key={code} id={`available-locale-${code}`} name={`__availableLocale_${code}__`} label={label} intent={intent} checked={checked} disabled={mutationsBlocked || disabled} onCheckedChange={(value) => toggle(code, value === true)}/>);
57
- })}
58
- </div>
59
- <span id="available-locales-description" className={cx('byline-form-available-locales-sr-only', styles['sr-only'])}>
60
- {t('availableLocalesWidget.srDescription')}
61
- </span>
62
- </div>);
63
- };