@umituz/web-design-system 6.3.0 → 6.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-design-system",
3
- "version": "6.3.0",
3
+ "version": "6.5.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Web Design System - Tailwind v4 authored, precompiled CSS shipped. Atomic Design components (Atoms, Molecules, Organisms, Templates) for React applications.",
@@ -80,6 +80,24 @@ describe("BarChart", () => {
80
80
  expect(goal.textContent).toContain("Hedef");
81
81
  });
82
82
 
83
+ it("renders a band via goalLines with each line placed independently", () => {
84
+ const { container } = render(
85
+ <BarChart
86
+ labels={labels}
87
+ goalLines={[
88
+ { value: 1, label: "Alt", tone: "#65c99a" },
89
+ { value: 3, label: "Üst", tone: "#65c99a" },
90
+ ]}
91
+ series={[{ label: "A", values: [4, 4, 4] }]}
92
+ />,
93
+ );
94
+ const goals = container.querySelectorAll(".udb-barchart__goal");
95
+ expect(goals).toHaveLength(2);
96
+ expect((goals[0] as HTMLElement).style.bottom).toBe("25%");
97
+ expect((goals[1] as HTMLElement).style.bottom).toBe("75%");
98
+ expect(goals[1]!.textContent).toContain("Üst");
99
+ });
100
+
83
101
  it("colors series through the --udb-chart tokens by default", () => {
84
102
  const { container } = render(
85
103
  <BarChart labels={labels} series={[{ label: "A", values: [1, 2, 3] }]} />,
@@ -0,0 +1,77 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
3
+ import { ConfirmProvider, useConfirm } from '../presentation/organisms/ConfirmProvider';
4
+
5
+ afterEach(cleanup);
6
+
7
+ function Probe({ onResult }: { onResult: (value: boolean) => void }) {
8
+ const confirm = useConfirm();
9
+ return (
10
+ <button
11
+ type="button"
12
+ onClick={() => {
13
+ void confirm({ title: 'Delete item?', body: 'This cannot be undone.' }).then(onResult);
14
+ }}
15
+ >
16
+ ask
17
+ </button>
18
+ );
19
+ }
20
+
21
+ describe('ConfirmProvider', () => {
22
+ it('resolves true when confirmed and closes the dialog', async () => {
23
+ let result: boolean | undefined;
24
+ render(
25
+ <ConfirmProvider>
26
+ <Probe onResult={(v) => { result = v; }} />
27
+ </ConfirmProvider>,
28
+ );
29
+ expect(screen.queryByRole('dialog')).toBeNull();
30
+ fireEvent.click(screen.getByRole('button', { name: 'ask' }));
31
+ expect(screen.getByRole('dialog')).toBeTruthy();
32
+ expect(screen.getByText('This cannot be undone.')).toBeTruthy();
33
+ fireEvent.click(screen.getByRole('button', { name: 'Confirm' }));
34
+ await waitFor(() => expect(result).toBe(true));
35
+ await waitFor(() => expect(screen.queryByRole('dialog')).toBeNull());
36
+ });
37
+
38
+ it('resolves false when cancelled', async () => {
39
+ let result: boolean | undefined;
40
+ render(
41
+ <ConfirmProvider>
42
+ <Probe onResult={(v) => { result = v; }} />
43
+ </ConfirmProvider>,
44
+ );
45
+ fireEvent.click(screen.getByRole('button', { name: 'ask' }));
46
+ fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
47
+ await waitFor(() => expect(result).toBe(false));
48
+ });
49
+
50
+ it('honors custom labels and tone', () => {
51
+ function LabelledProbe() {
52
+ const confirm = useConfirm();
53
+ return (
54
+ <button
55
+ type="button"
56
+ onClick={() => {
57
+ void confirm({ title: 'Leave page?', tone: 'danger', confirmLabel: 'Leave', cancelLabel: 'Stay' });
58
+ }}
59
+ >
60
+ go
61
+ </button>
62
+ );
63
+ }
64
+ render(
65
+ <ConfirmProvider>
66
+ <LabelledProbe />
67
+ </ConfirmProvider>,
68
+ );
69
+ fireEvent.click(screen.getByRole('button', { name: 'go' }));
70
+ expect(screen.getByRole('button', { name: 'Leave' })).toBeTruthy();
71
+ expect(screen.getByRole('button', { name: 'Stay' })).toBeTruthy();
72
+ });
73
+
74
+ it('throws when used outside the provider', () => {
75
+ expect(() => render(<Probe onResult={() => undefined} />)).toThrow(/ConfirmProvider/);
76
+ });
77
+ });
@@ -0,0 +1,56 @@
1
+
2
+ import { createContext, useCallback, useContext, useRef, useState } from 'react';
3
+ import type { ReactNode } from 'react';
4
+ import { ConfirmDialog } from './ConfirmDialog';
5
+
6
+ export interface ConfirmOptions {
7
+ title: ReactNode;
8
+ body?: ReactNode;
9
+ tone?: 'danger' | 'primary' | 'success';
10
+ confirmLabel?: string;
11
+ cancelLabel?: string;
12
+ }
13
+
14
+ export type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
15
+
16
+ const ConfirmContext = createContext<ConfirmFn | null>(null);
17
+
18
+ export function useConfirm(): ConfirmFn {
19
+ const confirm = useContext(ConfirmContext);
20
+ if (!confirm) throw new Error('useConfirm must be used within ConfirmProvider');
21
+ return confirm;
22
+ }
23
+
24
+ export function ConfirmProvider({ children }: { children: ReactNode }) {
25
+ const [options, setOptions] = useState<ConfirmOptions | null>(null);
26
+ const resolver = useRef<((value: boolean) => void) | null>(null);
27
+
28
+ const confirm = useCallback<ConfirmFn>((next) => {
29
+ setOptions(next);
30
+ return new Promise<boolean>((resolve) => {
31
+ resolver.current = resolve;
32
+ });
33
+ }, []);
34
+
35
+ const settle = (value: boolean) => {
36
+ resolver.current?.(value);
37
+ resolver.current = null;
38
+ setOptions(null);
39
+ };
40
+
41
+ return (
42
+ <ConfirmContext.Provider value={confirm}>
43
+ {children}
44
+ <ConfirmDialog
45
+ open={options !== null}
46
+ title={options?.title ?? ''}
47
+ body={options?.body}
48
+ tone={options?.tone ?? 'primary'}
49
+ confirmLabel={options?.confirmLabel}
50
+ cancelLabel={options?.cancelLabel}
51
+ onConfirm={() => settle(true)}
52
+ onClose={() => settle(false)}
53
+ />
54
+ </ConfirmContext.Provider>
55
+ );
56
+ }
@@ -26,6 +26,7 @@ export interface BarChartProps {
26
26
  formatValue?: (v: number) => string;
27
27
  formatLabel?: (l: string) => string;
28
28
  goalLine?: BarChartGoalLine;
29
+ goalLines?: readonly BarChartGoalLine[];
29
30
  }
30
31
 
31
32
  export function BarChart({
@@ -37,43 +38,45 @@ export function BarChart({
37
38
  formatValue = (v: number) => String(v),
38
39
  formatLabel = (l: string) => l,
39
40
  goalLine,
41
+ goalLines,
40
42
  }: BarChartProps) {
41
43
  const [hover, setHover] = useState<number | null>(null);
42
44
  const vals = (s: BarSeries) => s.values ?? s.data ?? [];
45
+ const goals = goalLines ?? (goalLine !== undefined ? [goalLine] : []);
43
46
  const columnTotals = labels.map((_, i) => series.reduce((sum, s) => sum + (vals(s)[i] ?? 0), 0));
44
47
  const max =
45
48
  stacking === 'stacked'
46
- ? Math.max(1, ...columnTotals, goalLine?.value ?? 0)
47
- : Math.max(1, ...series.flatMap((s) => vals(s)), goalLine?.value ?? 0);
49
+ ? Math.max(1, ...columnTotals, ...goals.map((g) => g.value))
50
+ : Math.max(1, ...series.flatMap((s) => vals(s)), ...goals.map((g) => g.value));
48
51
  const toneOf = (s: BarSeries, i: number) => s.tone ?? s.color ?? seriesColor(i);
49
52
  const accentOf = (s: BarSeries, si: number, i: number) => s.accents?.[i] ?? toneOf(s, si);
50
- const goalPct = goalLine !== undefined ? Math.min(100, (goalLine.value / max) * 100) : null;
51
53
  return (
52
54
  <div className="udb-barchart" style={{ height }}>
53
- {goalLine !== undefined && goalPct !== null && (
55
+ {goals.map((goal) => (
54
56
  <div
57
+ key={`${goal.value}-${goal.label ?? ''}`}
55
58
  className="udb-barchart__goal"
56
59
  style={{
57
- bottom: `${goalPct}%`,
58
- ...(goalLine.tone !== undefined ? { borderTopColor: goalLine.tone } : {}),
60
+ bottom: `${Math.min(100, (goal.value / max) * 100)}%`,
61
+ ...(goal.tone !== undefined ? { borderTopColor: goal.tone } : {}),
59
62
  }}
60
- title={goalLine.label ?? formatValue(goalLine.value)}
63
+ title={goal.label ?? formatValue(goal.value)}
61
64
  aria-hidden="true"
62
65
  >
63
- {goalLine.label !== undefined && (
66
+ {goal.label !== undefined && (
64
67
  <span
65
68
  className="udb-barchart__goallabel"
66
69
  style={
67
- goalLine.tone !== undefined
68
- ? { color: goalLine.tone, borderColor: goalLine.tone }
70
+ goal.tone !== undefined
71
+ ? { color: goal.tone, borderColor: goal.tone }
69
72
  : undefined
70
73
  }
71
74
  >
72
- {goalLine.label}
75
+ {goal.label}
73
76
  </span>
74
77
  )}
75
78
  </div>
76
- )}
79
+ ))}
77
80
  {labels.map((label, i) => (
78
81
  <div
79
82
  key={label}
@@ -170,6 +170,9 @@ export type {
170
170
  export { ConfirmDialog } from './ConfirmDialog';
171
171
  export type { ConfirmDialogProps } from './ConfirmDialog';
172
172
 
173
+ export { ConfirmProvider, useConfirm } from './ConfirmProvider';
174
+ export type { ConfirmOptions, ConfirmFn } from './ConfirmProvider';
175
+
173
176
  export { Stepper } from './Stepper';
174
177
  export type { StepperProps, StepperStep, StepperStepStatus } from './Stepper';
175
178