@xsolla/xui-gdpr-banner 0.206.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.
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # @xsolla/xui-gdpr-banner
2
+
3
+ The Xsolla cookie-consent (GDPR) banner. Implements the Figma `GDPR-banner`
4
+ component set. Also re-exported from `@xsolla/xui-feedback`.
5
+
6
+ Full API reference: [`docs/api/components/gdpr-banner.md`](../../../docs/api/components/gdpr-banner.md)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ yarn add @xsolla/xui-gdpr-banner
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```tsx
17
+ import { GdprBanner } from "@xsolla/xui-gdpr-banner";
18
+
19
+ <GdprBanner
20
+ cookiePolicyUrl="https://xsolla.com/cookie-policy"
21
+ privacyPolicyUrl="https://xsolla.com/privacy-policy"
22
+ onAcceptAll={() => consent.acceptAll()}
23
+ onRejectAll={() => consent.rejectAll()}
24
+ onConfirmSelection={(ids) => consent.save(ids)}
25
+ onClose={() => setVisible(false)}
26
+ />;
27
+ ```
28
+
29
+ ## Figma variant mapping
30
+
31
+ | Figma variant | Prop | Values |
32
+ | --- | --- | --- |
33
+ | `Type` | `type` | `"complex"` (default) \| `"simple"` |
34
+ | `Fold` | `expanded` | `false` (runtime default) \| `true` |
35
+ | `Buttons row` | `buttonsRow` | `"single"` (default) \| `"double"` |
36
+ | `Viewport` | `viewport` | `"desktop"` (default) \| `"mobile"` |
37
+
38
+ ## Layout
39
+
40
+ | State | Behaviour |
41
+ | --- | --- |
42
+ | Desktop, collapsed | 400px card pinned to a bottom corner with a 20px inset |
43
+ | Desktop, expanded | Full-bleed bottom bar with the two-column Privacy settings panel |
44
+ | Mobile | Bottom-anchored, full width, stacked actions |
45
+
46
+ ## Accessibility
47
+
48
+ `role="dialog"` labelled by the visible heading, focus pulled in on mount and on
49
+ expand, Tab/Shift+Tab cycling inside the banner, Escape fires `onClose`, and an
50
+ explicit `aria-label` on the close control.
@@ -0,0 +1,199 @@
1
+ import React from 'react';
2
+ import { ThemeOverrideProps } from '@xsolla/xui-core';
3
+
4
+ /**
5
+ * Banner density.
6
+ *
7
+ * - `complex` - full consent banner with `Customize` / `Reject All` /
8
+ * `Accept All` and an expandable Privacy settings panel.
9
+ * - `simple` - essential-cookies-only notice with a single `Dismiss` action.
10
+ *
11
+ * Maps to the Figma `Type` variant (`Complex` | `Simple`).
12
+ */
13
+ type GdprBannerType = "complex" | "simple";
14
+ /** Layout target. Maps to the Figma `Viewport` variant. */
15
+ type GdprBannerViewport = "desktop" | "mobile";
16
+ /**
17
+ * How the action buttons wrap.
18
+ *
19
+ * - `single` - `Customize` / `Reject All` / `Accept All` on one row.
20
+ * - `double` - `Customize` moves onto its own row above the other two, which
21
+ * keeps long translations from overflowing the card.
22
+ *
23
+ * Maps to the Figma `Buttons row` variant.
24
+ */
25
+ type GdprBannerButtonsRow = "single" | "double";
26
+ /**
27
+ * Which bottom corner the collapsed desktop card is pinned to. Ignored when
28
+ * the banner is expanded (the Privacy settings panel always spans full width)
29
+ * or when `viewport` is `mobile`.
30
+ */
31
+ type GdprBannerAnchor = "bottom-left" | "bottom-right";
32
+ /** A single consent category rendered inside the Privacy settings panel. */
33
+ interface GdprCookieCategory {
34
+ /** Stable identifier reported through `onSelectionChange` / `onConfirmSelection`. */
35
+ id: string;
36
+ /** Visible category name. */
37
+ label: string;
38
+ /** Long-form explanation shown in the description pane when selected. */
39
+ description?: string;
40
+ /**
41
+ * Always-on category (e.g. essential cookies). Rendered checked and
42
+ * disabled, and can never be removed from the selection.
43
+ */
44
+ locked?: boolean;
45
+ }
46
+ /** Every user-facing string, so consumers can localize the banner. */
47
+ interface GdprBannerLabels {
48
+ /** Banner heading. Default: `"How we use cookies"`. */
49
+ title?: string;
50
+ /** Body copy for `type="complex"`. */
51
+ description?: string;
52
+ /** Body copy for `type="simple"`. */
53
+ simpleDescription?: string;
54
+ /** Heading of the expanded Privacy settings panel. */
55
+ privacySettingsTitle?: string;
56
+ /** Label of the non-toggleable introductory row in the category list. */
57
+ generalInformation?: string;
58
+ /** Copy shown in the description pane while `General information` is selected. */
59
+ generalInformationDescription?: string;
60
+ customize?: string;
61
+ rejectAll?: string;
62
+ acceptAll?: string;
63
+ confirmSelection?: string;
64
+ dismiss?: string;
65
+ close?: string;
66
+ cookiePolicy?: string;
67
+ privacyPolicy?: string;
68
+ /** Accessible name of the icon-only close control. */
69
+ closeButtonAriaLabel?: string;
70
+ }
71
+ interface GdprBannerProps extends ThemeOverrideProps {
72
+ /** Banner density. @default "complex" */
73
+ type?: GdprBannerType;
74
+ /** Layout target. @default "desktop" */
75
+ viewport?: GdprBannerViewport;
76
+ /** Action-button wrapping. @default "single" */
77
+ buttonsRow?: GdprBannerButtonsRow;
78
+ /** Bottom corner for the collapsed desktop card. @default "bottom-left" */
79
+ anchor?: GdprBannerAnchor;
80
+ /**
81
+ * Whether the Privacy settings panel is open (the Figma `Fold` variant).
82
+ * Omit to let the banner manage the state itself; `Customize` opens it and
83
+ * `Close` collapses it back to the compact card.
84
+ *
85
+ * Note: the Figma component set defaults this variant to `On` for review
86
+ * convenience. At runtime the banner starts collapsed, which is the correct
87
+ * first-visit behaviour.
88
+ */
89
+ expanded?: boolean;
90
+ /** Initial open state when `expanded` is not supplied. @default false */
91
+ defaultExpanded?: boolean;
92
+ /** Fired whenever the panel opens or closes. */
93
+ onExpandedChange?: (expanded: boolean) => void;
94
+ /** Consent categories. Defaults to the four Xsolla categories from Figma. */
95
+ categories?: GdprCookieCategory[];
96
+ /** Controlled set of accepted category ids. */
97
+ selectedCategoryIds?: string[];
98
+ /** Initial accepted category ids when uncontrolled. Defaults to all of them. */
99
+ defaultSelectedCategoryIds?: string[];
100
+ /** Fired on every checkbox toggle with the next full selection. */
101
+ onSelectionChange?: (selectedCategoryIds: string[]) => void;
102
+ /** Overrides for any user-facing string. */
103
+ labels?: GdprBannerLabels;
104
+ /** Destination of the `Cookie Policy` footer link. */
105
+ cookiePolicyUrl?: string;
106
+ /** Destination of the `Privacy Policy` footer link. */
107
+ privacyPolicyUrl?: string;
108
+ /** `Accept All` pressed - every category consented. */
109
+ onAcceptAll?: () => void;
110
+ /** `Reject All` pressed - only locked categories remain consented. */
111
+ onRejectAll?: () => void;
112
+ /**
113
+ * `Customize` pressed. Fired in addition to the internal expand, so a
114
+ * consumer can log the interaction without taking over the state.
115
+ */
116
+ onCustomize?: () => void;
117
+ /** `Confirm Selection` pressed, with the accepted category ids. */
118
+ onConfirmSelection?: (selectedCategoryIds: string[]) => void;
119
+ /** `Dismiss` pressed on a `simple` banner. */
120
+ onDismiss?: () => void;
121
+ /** Close (X) pressed, or Escape. */
122
+ onClose?: () => void;
123
+ /** Render the close (X) control. @default true */
124
+ showCloseButton?: boolean;
125
+ /**
126
+ * `fixed` pins the banner to the bottom of the viewport (production usage).
127
+ * `static` renders it in normal flow, which is what stories and tests want.
128
+ * @default "fixed"
129
+ */
130
+ position?: "fixed" | "static";
131
+ /**
132
+ * Distance in px from the bottom / left / right edges while collapsed.
133
+ * Expanded panels always sit flush against the edges. @default 20
134
+ */
135
+ offset?: number;
136
+ testID?: string;
137
+ }
138
+ interface PrivacySettingsProps extends ThemeOverrideProps {
139
+ categories: GdprCookieCategory[];
140
+ selectedCategoryIds: string[];
141
+ onToggleCategory: (id: string) => void;
142
+ /** Id of the category whose description is shown, or `null` for General information. */
143
+ activeCategoryId: string | null;
144
+ onActiveCategoryChange: (id: string | null) => void;
145
+ viewport: GdprBannerViewport;
146
+ labels: Required<Pick<GdprBannerLabels, "generalInformation" | "generalInformationDescription" | "cookiePolicy" | "privacyPolicy">>;
147
+ cookiePolicyUrl?: string;
148
+ privacyPolicyUrl?: string;
149
+ testID?: string;
150
+ }
151
+
152
+ /**
153
+ * GdprBanner - the Xsolla cookie-consent banner.
154
+ *
155
+ * Implements the Figma `GDPR-banner` component set. The four Figma variant
156
+ * axes map to props: `Type` -> {@link GdprBannerProps.type}, `Fold` ->
157
+ * {@link GdprBannerProps.expanded} (inverted - Figma's `Fold=On` is the
158
+ * collapsed card), `Buttons row` -> {@link GdprBannerProps.buttonsRow},
159
+ * `Viewport` -> {@link GdprBannerProps.viewport}.
160
+ *
161
+ * ## Layout
162
+ *
163
+ * - Desktop, collapsed - a card pinned to the bottom of the viewport with a
164
+ * 20px inset, at either bottom corner (`anchor`).
165
+ * - Desktop, expanded - a wider panel hosting the two-column Privacy settings.
166
+ * - Mobile - always bottom-anchored and full width; actions stack.
167
+ *
168
+ * `Customize` and `Close` are borderless brand-tone text actions, matching the
169
+ * Figma `Flex Button` with `Background=False`.
170
+ *
171
+ * ## Accessibility
172
+ *
173
+ * - `role="dialog"` labelled by the visible heading.
174
+ * - Focus moves into the banner when it mounts and when the panel expands.
175
+ * - Tab and Shift+Tab cycle within the banner; Escape fires `onClose`.
176
+ * - The close control carries an explicit `aria-label`.
177
+ */
178
+ declare const GdprBanner: React.FC<GdprBannerProps>;
179
+
180
+ /**
181
+ * The expanded half of the GDPR banner: a category list on the left and the
182
+ * selected category's explanation on the right (stacked on mobile, where the
183
+ * explanation renders directly beneath the row it belongs to).
184
+ *
185
+ * Rendered by `GdprBanner` when the Privacy settings panel is open; exported
186
+ * so consumers can embed the same panel inside their own settings screen.
187
+ */
188
+ declare const PrivacySettings: React.FC<PrivacySettingsProps>;
189
+
190
+ /**
191
+ * The five categories shown in the Figma Privacy settings panel.
192
+ *
193
+ * `essential` is `locked`: it renders checked + disabled and is always part of
194
+ * the reported selection, including after `Reject All`.
195
+ */
196
+ declare const DEFAULT_CATEGORIES: GdprCookieCategory[];
197
+ declare const DEFAULT_LABELS: Required<GdprBannerLabels>;
198
+
199
+ export { DEFAULT_CATEGORIES, DEFAULT_LABELS, GdprBanner, type GdprBannerAnchor, type GdprBannerButtonsRow, type GdprBannerLabels, type GdprBannerProps, type GdprBannerType, type GdprBannerViewport, type GdprCookieCategory, PrivacySettings, type PrivacySettingsProps };
@@ -0,0 +1,199 @@
1
+ import React from 'react';
2
+ import { ThemeOverrideProps } from '@xsolla/xui-core';
3
+
4
+ /**
5
+ * Banner density.
6
+ *
7
+ * - `complex` - full consent banner with `Customize` / `Reject All` /
8
+ * `Accept All` and an expandable Privacy settings panel.
9
+ * - `simple` - essential-cookies-only notice with a single `Dismiss` action.
10
+ *
11
+ * Maps to the Figma `Type` variant (`Complex` | `Simple`).
12
+ */
13
+ type GdprBannerType = "complex" | "simple";
14
+ /** Layout target. Maps to the Figma `Viewport` variant. */
15
+ type GdprBannerViewport = "desktop" | "mobile";
16
+ /**
17
+ * How the action buttons wrap.
18
+ *
19
+ * - `single` - `Customize` / `Reject All` / `Accept All` on one row.
20
+ * - `double` - `Customize` moves onto its own row above the other two, which
21
+ * keeps long translations from overflowing the card.
22
+ *
23
+ * Maps to the Figma `Buttons row` variant.
24
+ */
25
+ type GdprBannerButtonsRow = "single" | "double";
26
+ /**
27
+ * Which bottom corner the collapsed desktop card is pinned to. Ignored when
28
+ * the banner is expanded (the Privacy settings panel always spans full width)
29
+ * or when `viewport` is `mobile`.
30
+ */
31
+ type GdprBannerAnchor = "bottom-left" | "bottom-right";
32
+ /** A single consent category rendered inside the Privacy settings panel. */
33
+ interface GdprCookieCategory {
34
+ /** Stable identifier reported through `onSelectionChange` / `onConfirmSelection`. */
35
+ id: string;
36
+ /** Visible category name. */
37
+ label: string;
38
+ /** Long-form explanation shown in the description pane when selected. */
39
+ description?: string;
40
+ /**
41
+ * Always-on category (e.g. essential cookies). Rendered checked and
42
+ * disabled, and can never be removed from the selection.
43
+ */
44
+ locked?: boolean;
45
+ }
46
+ /** Every user-facing string, so consumers can localize the banner. */
47
+ interface GdprBannerLabels {
48
+ /** Banner heading. Default: `"How we use cookies"`. */
49
+ title?: string;
50
+ /** Body copy for `type="complex"`. */
51
+ description?: string;
52
+ /** Body copy for `type="simple"`. */
53
+ simpleDescription?: string;
54
+ /** Heading of the expanded Privacy settings panel. */
55
+ privacySettingsTitle?: string;
56
+ /** Label of the non-toggleable introductory row in the category list. */
57
+ generalInformation?: string;
58
+ /** Copy shown in the description pane while `General information` is selected. */
59
+ generalInformationDescription?: string;
60
+ customize?: string;
61
+ rejectAll?: string;
62
+ acceptAll?: string;
63
+ confirmSelection?: string;
64
+ dismiss?: string;
65
+ close?: string;
66
+ cookiePolicy?: string;
67
+ privacyPolicy?: string;
68
+ /** Accessible name of the icon-only close control. */
69
+ closeButtonAriaLabel?: string;
70
+ }
71
+ interface GdprBannerProps extends ThemeOverrideProps {
72
+ /** Banner density. @default "complex" */
73
+ type?: GdprBannerType;
74
+ /** Layout target. @default "desktop" */
75
+ viewport?: GdprBannerViewport;
76
+ /** Action-button wrapping. @default "single" */
77
+ buttonsRow?: GdprBannerButtonsRow;
78
+ /** Bottom corner for the collapsed desktop card. @default "bottom-left" */
79
+ anchor?: GdprBannerAnchor;
80
+ /**
81
+ * Whether the Privacy settings panel is open (the Figma `Fold` variant).
82
+ * Omit to let the banner manage the state itself; `Customize` opens it and
83
+ * `Close` collapses it back to the compact card.
84
+ *
85
+ * Note: the Figma component set defaults this variant to `On` for review
86
+ * convenience. At runtime the banner starts collapsed, which is the correct
87
+ * first-visit behaviour.
88
+ */
89
+ expanded?: boolean;
90
+ /** Initial open state when `expanded` is not supplied. @default false */
91
+ defaultExpanded?: boolean;
92
+ /** Fired whenever the panel opens or closes. */
93
+ onExpandedChange?: (expanded: boolean) => void;
94
+ /** Consent categories. Defaults to the four Xsolla categories from Figma. */
95
+ categories?: GdprCookieCategory[];
96
+ /** Controlled set of accepted category ids. */
97
+ selectedCategoryIds?: string[];
98
+ /** Initial accepted category ids when uncontrolled. Defaults to all of them. */
99
+ defaultSelectedCategoryIds?: string[];
100
+ /** Fired on every checkbox toggle with the next full selection. */
101
+ onSelectionChange?: (selectedCategoryIds: string[]) => void;
102
+ /** Overrides for any user-facing string. */
103
+ labels?: GdprBannerLabels;
104
+ /** Destination of the `Cookie Policy` footer link. */
105
+ cookiePolicyUrl?: string;
106
+ /** Destination of the `Privacy Policy` footer link. */
107
+ privacyPolicyUrl?: string;
108
+ /** `Accept All` pressed - every category consented. */
109
+ onAcceptAll?: () => void;
110
+ /** `Reject All` pressed - only locked categories remain consented. */
111
+ onRejectAll?: () => void;
112
+ /**
113
+ * `Customize` pressed. Fired in addition to the internal expand, so a
114
+ * consumer can log the interaction without taking over the state.
115
+ */
116
+ onCustomize?: () => void;
117
+ /** `Confirm Selection` pressed, with the accepted category ids. */
118
+ onConfirmSelection?: (selectedCategoryIds: string[]) => void;
119
+ /** `Dismiss` pressed on a `simple` banner. */
120
+ onDismiss?: () => void;
121
+ /** Close (X) pressed, or Escape. */
122
+ onClose?: () => void;
123
+ /** Render the close (X) control. @default true */
124
+ showCloseButton?: boolean;
125
+ /**
126
+ * `fixed` pins the banner to the bottom of the viewport (production usage).
127
+ * `static` renders it in normal flow, which is what stories and tests want.
128
+ * @default "fixed"
129
+ */
130
+ position?: "fixed" | "static";
131
+ /**
132
+ * Distance in px from the bottom / left / right edges while collapsed.
133
+ * Expanded panels always sit flush against the edges. @default 20
134
+ */
135
+ offset?: number;
136
+ testID?: string;
137
+ }
138
+ interface PrivacySettingsProps extends ThemeOverrideProps {
139
+ categories: GdprCookieCategory[];
140
+ selectedCategoryIds: string[];
141
+ onToggleCategory: (id: string) => void;
142
+ /** Id of the category whose description is shown, or `null` for General information. */
143
+ activeCategoryId: string | null;
144
+ onActiveCategoryChange: (id: string | null) => void;
145
+ viewport: GdprBannerViewport;
146
+ labels: Required<Pick<GdprBannerLabels, "generalInformation" | "generalInformationDescription" | "cookiePolicy" | "privacyPolicy">>;
147
+ cookiePolicyUrl?: string;
148
+ privacyPolicyUrl?: string;
149
+ testID?: string;
150
+ }
151
+
152
+ /**
153
+ * GdprBanner - the Xsolla cookie-consent banner.
154
+ *
155
+ * Implements the Figma `GDPR-banner` component set. The four Figma variant
156
+ * axes map to props: `Type` -> {@link GdprBannerProps.type}, `Fold` ->
157
+ * {@link GdprBannerProps.expanded} (inverted - Figma's `Fold=On` is the
158
+ * collapsed card), `Buttons row` -> {@link GdprBannerProps.buttonsRow},
159
+ * `Viewport` -> {@link GdprBannerProps.viewport}.
160
+ *
161
+ * ## Layout
162
+ *
163
+ * - Desktop, collapsed - a card pinned to the bottom of the viewport with a
164
+ * 20px inset, at either bottom corner (`anchor`).
165
+ * - Desktop, expanded - a wider panel hosting the two-column Privacy settings.
166
+ * - Mobile - always bottom-anchored and full width; actions stack.
167
+ *
168
+ * `Customize` and `Close` are borderless brand-tone text actions, matching the
169
+ * Figma `Flex Button` with `Background=False`.
170
+ *
171
+ * ## Accessibility
172
+ *
173
+ * - `role="dialog"` labelled by the visible heading.
174
+ * - Focus moves into the banner when it mounts and when the panel expands.
175
+ * - Tab and Shift+Tab cycle within the banner; Escape fires `onClose`.
176
+ * - The close control carries an explicit `aria-label`.
177
+ */
178
+ declare const GdprBanner: React.FC<GdprBannerProps>;
179
+
180
+ /**
181
+ * The expanded half of the GDPR banner: a category list on the left and the
182
+ * selected category's explanation on the right (stacked on mobile, where the
183
+ * explanation renders directly beneath the row it belongs to).
184
+ *
185
+ * Rendered by `GdprBanner` when the Privacy settings panel is open; exported
186
+ * so consumers can embed the same panel inside their own settings screen.
187
+ */
188
+ declare const PrivacySettings: React.FC<PrivacySettingsProps>;
189
+
190
+ /**
191
+ * The five categories shown in the Figma Privacy settings panel.
192
+ *
193
+ * `essential` is `locked`: it renders checked + disabled and is always part of
194
+ * the reported selection, including after `Reject All`.
195
+ */
196
+ declare const DEFAULT_CATEGORIES: GdprCookieCategory[];
197
+ declare const DEFAULT_LABELS: Required<GdprBannerLabels>;
198
+
199
+ export { DEFAULT_CATEGORIES, DEFAULT_LABELS, GdprBanner, type GdprBannerAnchor, type GdprBannerButtonsRow, type GdprBannerLabels, type GdprBannerProps, type GdprBannerType, type GdprBannerViewport, type GdprCookieCategory, PrivacySettings, type PrivacySettingsProps };