@pcoi/components 0.1.0 → 0.1.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.
Files changed (60) hide show
  1. package/dist/components.css +1 -1
  2. package/dist/index.d.ts +5 -6
  3. package/dist/index.js +2 -2
  4. package/dist/index.mjs +296 -375
  5. package/package.json +12 -5
  6. package/src/Badge/Badge.css +2 -2
  7. package/src/Button/Button.css +4 -4
  8. package/src/Button/Button.figma.tsx +3 -5
  9. package/src/Button/Button.test.tsx +32 -0
  10. package/src/Callout/Callout.css +5 -5
  11. package/src/Callout/Callout.figma.tsx +25 -0
  12. package/src/Card/Card.css +8 -8
  13. package/src/Card/Card.figma.tsx +28 -0
  14. package/src/ChatInterface/ChatInterface.css +5 -5
  15. package/src/ChatInterface/ChatInterface.integration.test.tsx +123 -0
  16. package/src/ChatMessage/ChatMessage.css +8 -8
  17. package/src/ChatMessageList/ChatMessageList.css +4 -4
  18. package/src/ChatMessageList/ChatMessageList.test.tsx +74 -0
  19. package/src/Checkbox/Checkbox.css +6 -6
  20. package/src/CitationMark/CitationMark.css +3 -3
  21. package/src/CitedExcerpt/CitedExcerpt.css +7 -7
  22. package/src/ComparisonTable/ComparisonTable.css +6 -6
  23. package/src/ContactForm/ContactForm.css +5 -5
  24. package/src/ContactForm/ContactForm.tsx +1 -1
  25. package/src/DataTable/DataTable.css +4 -4
  26. package/src/DocumentOverlay/DocumentOverlay.css +5 -5
  27. package/src/DocumentOverlay/DocumentOverlay.test.tsx +95 -0
  28. package/src/DocumentOverlay/DocumentOverlay.tsx +1 -1
  29. package/src/Footer/Footer.css +9 -9
  30. package/src/FormField/FormField.css +4 -4
  31. package/src/FormField/FormField.figma.tsx +28 -0
  32. package/src/HowStep/HowStep.css +4 -4
  33. package/src/HowStep/HowStep.figma.tsx +23 -0
  34. package/src/LogoMark/LogoMark.tsx +1 -2
  35. package/src/Modal/Modal.css +11 -11
  36. package/src/Modal/Modal.figma.tsx +28 -0
  37. package/src/Modal/Modal.test.tsx +46 -0
  38. package/src/Modal/Modal.tsx +17 -19
  39. package/src/Nav/Nav.css +16 -16
  40. package/src/Panel/Panel.css +3 -3
  41. package/src/PromptBar/PromptBar.css +10 -10
  42. package/src/PromptBar/PromptBar.figma.tsx +25 -0
  43. package/src/PromptBar/PromptBar.test.tsx +83 -0
  44. package/src/PromptBar/PromptBar.tsx +2 -2
  45. package/src/RadioGroup/RadioGroup.css +11 -11
  46. package/src/SectionHeader/SectionHeader.css +4 -4
  47. package/src/SectionHeader/SectionHeader.figma.tsx +23 -0
  48. package/src/Select/Select.css +5 -5
  49. package/src/Select/Select.figma.tsx +33 -0
  50. package/src/Select/Select.tsx +1 -1
  51. package/src/SignalsPanel/SignalsPanel.css +9 -9
  52. package/src/SuggestionCard/SuggestionCard.css +5 -5
  53. package/src/SuggestionCards/SuggestionCards.css +1 -1
  54. package/src/SuggestionCards/SuggestionCards.test.tsx +27 -0
  55. package/src/SuggestionCards/SuggestionCards.tsx +1 -1
  56. package/src/Toast/Toast.css +14 -14
  57. package/src/Toast/Toast.tsx +1 -1
  58. package/src/Toggle/Toggle.css +15 -15
  59. package/src/Toggle/Toggle.figma.tsx +24 -0
  60. package/src/TypingIndicator/TypingIndicator.css +6 -6
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useRef, useCallback } from "react";
2
2
  import { createPortal } from "react-dom";
3
- import { CloseIcon } from "../../../icons/src/react/CloseIcon";
3
+ import { CloseIcon } from "@pcoi/icons";
4
4
  import type { HeadingLevel } from "../types";
5
5
 
6
6
  export interface ModalProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
@@ -15,7 +15,7 @@ export interface ModalProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "
15
15
  /** Dialog width: "default" (520px) or "wide" (900px) */
16
16
  size?: "default" | "wide";
17
17
  /** Modal body content */
18
- children: React.ReactNode;
18
+ children?: React.ReactNode;
19
19
  /** Footer content (e.g. action buttons) */
20
20
  footer?: React.ReactNode;
21
21
  }
@@ -112,23 +112,21 @@ export const Modal: React.FC<ModalProps> = ({
112
112
  aria-labelledby={titleId}
113
113
  {...rest}
114
114
  >
115
- {(title || true) && (
116
- <div className="pcoi-modal__header">
117
- {title && (
118
- <Heading id={titleId} className="pcoi-modal__title">
119
- {title}
120
- </Heading>
121
- )}
122
- <button
123
- type="button"
124
- className="pcoi-modal__close"
125
- onClick={onClose}
126
- aria-label="Close modal"
127
- >
128
- <CloseIcon size={20} />
129
- </button>
130
- </div>
131
- )}
115
+ <div className="pcoi-modal__header">
116
+ {title && (
117
+ <Heading id={titleId} className="pcoi-modal__title">
118
+ {title}
119
+ </Heading>
120
+ )}
121
+ <button
122
+ type="button"
123
+ className="pcoi-modal__close"
124
+ onClick={onClose}
125
+ aria-label="Close modal"
126
+ >
127
+ <CloseIcon size={20} />
128
+ </button>
129
+ </div>
132
130
  <div className="pcoi-modal__body">{children}</div>
133
131
  {footer && <div className="pcoi-modal__footer">{footer}</div>}
134
132
  </div>
package/src/Nav/Nav.css CHANGED
@@ -5,8 +5,8 @@
5
5
  top: 0;
6
6
  left: 0;
7
7
  right: 0;
8
- z-index: var(--pcoi-layout-zIndex-nav, 1000);
9
- height: var(--pcoi-layout-nav-height, 72px);
8
+ z-index: var(--pcoi-semantic-layout-z-nav, 1000);
9
+ height: var(--pcoi-semantic-spacing-nav-height, 72px);
10
10
  transition: background var(--pcoi-effect-transition-medium, 0.3s ease),
11
11
  box-shadow var(--pcoi-effect-transition-medium, 0.3s ease);
12
12
  }
@@ -19,9 +19,9 @@
19
19
  }
20
20
 
21
21
  .pcoi-nav__inner {
22
- max-width: var(--pcoi-layout-container-max);
22
+ max-width: var(--pcoi-semantic-sizing-container-max);
23
23
  margin: 0 auto;
24
- padding: 0 var(--pcoi-spacing-24);
24
+ padding: 0 var(--pcoi-semantic-spacing-btn-x);
25
25
  height: 100%;
26
26
  display: flex;
27
27
  align-items: center;
@@ -32,7 +32,7 @@
32
32
  .pcoi-nav__links {
33
33
  display: flex;
34
34
  align-items: center;
35
- gap: var(--pcoi-spacing-32);
35
+ gap: var(--pcoi-semantic-spacing-stack-lg);
36
36
  }
37
37
 
38
38
  .pcoi-nav__links a {
@@ -50,7 +50,7 @@
50
50
  .pcoi-nav__links a:focus-visible {
51
51
  outline: none;
52
52
  box-shadow: var(--pcoi-effect-shadow-focus-ring);
53
- border-radius: var(--pcoi-radius-sm);
53
+ border-radius: var(--pcoi-semantic-radius-btn);
54
54
  }
55
55
 
56
56
  .pcoi-nav__links a:active {
@@ -62,9 +62,9 @@
62
62
  display: none;
63
63
  flex-direction: column;
64
64
  justify-content: center;
65
- gap: var(--pcoi-spacing-5);
66
- width: var(--pcoi-layout-component-hamburger);
67
- height: var(--pcoi-layout-component-hamburger);
65
+ gap: var(--pcoi-semantic-spacing-hamburger-gap);
66
+ width: var(--pcoi-semantic-sizing-hamburger-size);
67
+ height: var(--pcoi-semantic-sizing-hamburger-size);
68
68
  background: none;
69
69
  border: none;
70
70
  cursor: pointer;
@@ -78,7 +78,7 @@
78
78
  .pcoi-nav__hamburger:focus-visible {
79
79
  outline: none;
80
80
  box-shadow: var(--pcoi-effect-shadow-focus-ring);
81
- border-radius: var(--pcoi-radius-sm);
81
+ border-radius: var(--pcoi-semantic-radius-btn);
82
82
  }
83
83
 
84
84
  .pcoi-nav__hamburger:active {
@@ -96,7 +96,7 @@
96
96
  }
97
97
 
98
98
  .pcoi-nav__hamburger--active span:nth-child(1) {
99
- transform: translateY(var(--pcoi-layout-component-hamburger-bar-offset)) rotate(45deg);
99
+ transform: translateY(var(--pcoi-semantic-spacing-hamburger-bar-offset)) rotate(45deg);
100
100
  }
101
101
 
102
102
  .pcoi-nav__hamburger--active span:nth-child(2) {
@@ -104,14 +104,14 @@
104
104
  }
105
105
 
106
106
  .pcoi-nav__hamburger--active span:nth-child(3) {
107
- transform: translateY(calc(-1 * var(--pcoi-layout-component-hamburger-bar-offset))) rotate(-45deg);
107
+ transform: translateY(calc(-1 * var(--pcoi-semantic-spacing-hamburger-bar-offset))) rotate(-45deg);
108
108
  }
109
109
 
110
110
  /* ── Mobile Menu ── */
111
111
  .pcoi-nav__mobile-menu {
112
112
  display: none;
113
113
  flex-direction: column;
114
- padding: var(--pcoi-spacing-24) var(--pcoi-spacing-24) var(--pcoi-spacing-32);
114
+ padding: var(--pcoi-semantic-spacing-btn-x) var(--pcoi-semantic-spacing-btn-x) var(--pcoi-semantic-spacing-stack-lg);
115
115
  background: var(--pcoi-semantic-bg-default);
116
116
  box-shadow: var(--pcoi-effect-shadow-mobile-menu);
117
117
  }
@@ -121,7 +121,7 @@
121
121
  font-weight: var(--pcoi-semantic-type-label-weight);
122
122
  color: var(--pcoi-semantic-text-secondary);
123
123
  text-decoration: none;
124
- padding: var(--pcoi-spacing-12) 0;
124
+ padding: var(--pcoi-semantic-spacing-input-y) 0;
125
125
  transition: color var(--pcoi-effect-transition-fast, 0.2s ease);
126
126
  }
127
127
 
@@ -132,7 +132,7 @@
132
132
  .pcoi-nav__mobile-menu a:focus-visible {
133
133
  outline: none;
134
134
  box-shadow: var(--pcoi-effect-shadow-focus-ring);
135
- border-radius: var(--pcoi-radius-sm);
135
+ border-radius: var(--pcoi-semantic-radius-btn);
136
136
  }
137
137
 
138
138
  .pcoi-nav__mobile-menu a:active {
@@ -140,7 +140,7 @@
140
140
  }
141
141
 
142
142
  .pcoi-nav__mobile-menu .pcoi-btn {
143
- margin-top: var(--pcoi-spacing-16);
143
+ margin-top: var(--pcoi-semantic-spacing-stack-md);
144
144
  width: 100%;
145
145
  justify-content: center;
146
146
  }
@@ -3,7 +3,7 @@
3
3
  .pcoi-panel {
4
4
  background: var(--pcoi-semantic-bg-card);
5
5
  border: 1px solid var(--pcoi-semantic-border-default);
6
- border-radius: var(--pcoi-radius-md);
6
+ border-radius: var(--pcoi-semantic-radius-card);
7
7
  overflow: hidden;
8
8
  }
9
9
 
@@ -11,7 +11,7 @@
11
11
  display: flex;
12
12
  align-items: center;
13
13
  justify-content: space-between;
14
- padding: var(--pcoi-spacing-16) var(--pcoi-spacing-24);
14
+ padding: var(--pcoi-semantic-spacing-panel-padding-sm) var(--pcoi-semantic-spacing-panel-padding);
15
15
  border-bottom: 1px solid var(--pcoi-semantic-border-default);
16
16
  }
17
17
 
@@ -27,7 +27,7 @@
27
27
  }
28
28
 
29
29
  .pcoi-panel__body {
30
- padding: var(--pcoi-spacing-24);
30
+ padding: var(--pcoi-semantic-spacing-panel-padding);
31
31
  }
32
32
 
33
33
  .pcoi-panel--flush .pcoi-panel__body {
@@ -3,11 +3,11 @@
3
3
  .pcoi-prompt-bar {
4
4
  display: flex;
5
5
  align-items: flex-end;
6
- gap: var(--pcoi-spacing-12);
7
- padding: var(--pcoi-spacing-12) var(--pcoi-spacing-16);
8
- background: var(--pcoi-color-bg-card);
6
+ gap: var(--pcoi-semantic-spacing-panel-gap);
7
+ padding: var(--pcoi-semantic-spacing-input-y) var(--pcoi-semantic-spacing-input-x);
8
+ background: var(--pcoi-semantic-bg-card);
9
9
  border: 1px solid var(--pcoi-semantic-border-default);
10
- border-radius: var(--pcoi-radius-md);
10
+ border-radius: var(--pcoi-semantic-radius-card);
11
11
  transition:
12
12
  border-color var(--pcoi-effect-transition-fast),
13
13
  box-shadow var(--pcoi-effect-transition-fast);
@@ -18,7 +18,7 @@
18
18
  }
19
19
 
20
20
  .pcoi-prompt-bar:focus-within {
21
- border-color: var(--pcoi-color-focus-border);
21
+ border-color: var(--pcoi-semantic-focus-border);
22
22
  box-shadow: var(--pcoi-effect-shadow-focus-ring);
23
23
  }
24
24
 
@@ -26,7 +26,7 @@
26
26
  flex: 1;
27
27
  min-height: 24px;
28
28
  max-height: 120px;
29
- padding: var(--pcoi-spacing-4) 0;
29
+ padding: var(--pcoi-semantic-spacing-control-padding-2xs) 0;
30
30
  font-family: var(--pcoi-semantic-type-body-font);
31
31
  font-size: var(--pcoi-semantic-type-body-size);
32
32
  line-height: var(--pcoi-semantic-type-body-line-height);
@@ -48,11 +48,11 @@
48
48
  }
49
49
 
50
50
  .pcoi-prompt-bar__send.pcoi-btn {
51
- padding: var(--pcoi-spacing-6);
51
+ padding: var(--pcoi-semantic-spacing-control-padding-xs);
52
52
  min-width: 0;
53
53
  min-height: 0;
54
- width: 32px;
55
- height: 32px;
54
+ width: var(--pcoi-semantic-sizing-btn-height-nav);
55
+ height: var(--pcoi-semantic-sizing-btn-height-nav);
56
56
  }
57
57
 
58
58
  .pcoi-prompt-bar__send-icon {
@@ -63,6 +63,6 @@
63
63
  /* ── Responsive: compact send on small mobile ── */
64
64
  @media (max-width: 480px) {
65
65
  .pcoi-prompt-bar {
66
- padding: var(--pcoi-spacing-8) var(--pcoi-spacing-12);
66
+ padding: var(--pcoi-semantic-spacing-control-padding-sm) var(--pcoi-semantic-spacing-input-y);
67
67
  }
68
68
  }
@@ -0,0 +1,25 @@
1
+ import figma from "@figma/code-connect";
2
+ import { PromptBar } from "./PromptBar";
3
+
4
+ /**
5
+ * Code Connect: PromptBar
6
+ * Maps Figma prompt bar states to React PromptBar props
7
+ */
8
+ figma.connect(PromptBar, "https://www.figma.com/file/PCOIxDesignSystem/PCOI-Design-System?node-id=105-1", {
9
+ props: {
10
+ value: figma.string("Value"),
11
+ placeholder: figma.string("Placeholder"),
12
+ disabled: figma.boolean("Disabled"),
13
+ loading: figma.boolean("Loading"),
14
+ },
15
+ example: ({ value, placeholder, disabled, loading }) => (
16
+ <PromptBar
17
+ value={value}
18
+ placeholder={placeholder}
19
+ disabled={disabled}
20
+ loading={loading}
21
+ onChange={() => undefined}
22
+ onSubmit={() => undefined}
23
+ />
24
+ ),
25
+ });
@@ -0,0 +1,83 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { PromptBar } from "./PromptBar";
4
+
5
+ describe("PromptBar", () => {
6
+ it("calls onChange with textarea value", () => {
7
+ const onChange = vi.fn();
8
+ const onSubmit = vi.fn();
9
+
10
+ render(
11
+ <PromptBar
12
+ value=""
13
+ onChange={onChange}
14
+ onSubmit={onSubmit}
15
+ placeholder="Ask"
16
+ />
17
+ );
18
+
19
+ fireEvent.change(screen.getByRole("textbox", { name: "Ask" }), {
20
+ target: { value: "hello" },
21
+ });
22
+
23
+ expect(onChange).toHaveBeenCalledWith("hello");
24
+ });
25
+
26
+ it("submits trimmed value on Enter", () => {
27
+ const onChange = vi.fn();
28
+ const onSubmit = vi.fn();
29
+
30
+ render(
31
+ <PromptBar
32
+ value=" hello world "
33
+ onChange={onChange}
34
+ onSubmit={onSubmit}
35
+ placeholder="Ask"
36
+ />
37
+ );
38
+
39
+ fireEvent.keyDown(screen.getByRole("textbox", { name: "Ask" }), {
40
+ key: "Enter",
41
+ code: "Enter",
42
+ shiftKey: false,
43
+ });
44
+
45
+ expect(onSubmit).toHaveBeenCalledTimes(1);
46
+ expect(onSubmit).toHaveBeenCalledWith("hello world");
47
+ });
48
+
49
+ it("does not submit on Shift+Enter", () => {
50
+ const onChange = vi.fn();
51
+ const onSubmit = vi.fn();
52
+
53
+ render(
54
+ <PromptBar
55
+ value="hello"
56
+ onChange={onChange}
57
+ onSubmit={onSubmit}
58
+ placeholder="Ask"
59
+ />
60
+ );
61
+
62
+ fireEvent.keyDown(screen.getByRole("textbox", { name: "Ask" }), {
63
+ key: "Enter",
64
+ code: "Enter",
65
+ shiftKey: true,
66
+ });
67
+
68
+ expect(onSubmit).not.toHaveBeenCalled();
69
+ });
70
+
71
+ it("disables send button when value is empty/whitespace", () => {
72
+ render(
73
+ <PromptBar
74
+ value=" "
75
+ onChange={vi.fn()}
76
+ onSubmit={vi.fn()}
77
+ placeholder="Ask"
78
+ />
79
+ );
80
+
81
+ expect(screen.getByRole("button", { name: "Send message" })).toBeDisabled();
82
+ });
83
+ });
@@ -1,8 +1,8 @@
1
1
  import React, { useRef, useCallback } from "react";
2
2
  import { Button } from "../Button/Button";
3
- import { ArrowRightIcon } from "../../../icons/src/react/ArrowRightIcon";
3
+ import { ArrowRightIcon } from "@pcoi/icons";
4
4
 
5
- export interface PromptBarProps extends React.HTMLAttributes<HTMLDivElement> {
5
+ export interface PromptBarProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange" | "onSubmit"> {
6
6
  /** Current textarea value */
7
7
  value: string;
8
8
  /** Called when the textarea value changes */
@@ -6,7 +6,7 @@
6
6
  margin: 0;
7
7
  display: flex;
8
8
  flex-direction: column;
9
- gap: var(--pcoi-spacing-10);
9
+ gap: var(--pcoi-semantic-spacing-text-gap-sm);
10
10
  }
11
11
 
12
12
  .pcoi-radio-group__legend {
@@ -15,24 +15,24 @@
15
15
  letter-spacing: var(--pcoi-semantic-type-label-letter-spacing);
16
16
  text-transform: uppercase;
17
17
  color: var(--pcoi-semantic-text-secondary);
18
- margin-bottom: var(--pcoi-spacing-4);
18
+ margin-bottom: var(--pcoi-semantic-spacing-inline-2xs);
19
19
  }
20
20
 
21
21
  .pcoi-radio-group__required {
22
22
  color: var(--pcoi-semantic-text-error);
23
- margin-left: var(--pcoi-spacing-4);
23
+ margin-left: var(--pcoi-semantic-spacing-inline-2xs);
24
24
  }
25
25
 
26
26
  .pcoi-radio-group__options {
27
27
  display: flex;
28
28
  flex-direction: column;
29
- gap: var(--pcoi-spacing-12);
29
+ gap: var(--pcoi-semantic-spacing-panel-gap);
30
30
  }
31
31
 
32
32
  .pcoi-radio-group__option {
33
33
  display: inline-flex;
34
34
  align-items: center;
35
- gap: var(--pcoi-spacing-8);
35
+ gap: var(--pcoi-semantic-spacing-inline-sm);
36
36
  cursor: pointer;
37
37
  }
38
38
 
@@ -49,11 +49,11 @@
49
49
  }
50
50
 
51
51
  .pcoi-radio-group__circle {
52
- width: var(--pcoi-layout-component-control-box);
53
- height: var(--pcoi-layout-component-control-box);
52
+ width: var(--pcoi-semantic-sizing-control-box-size);
53
+ height: var(--pcoi-semantic-sizing-control-box-size);
54
54
  flex-shrink: 0;
55
55
  border: 1px solid var(--pcoi-semantic-border-default);
56
- border-radius: var(--pcoi-radius-full);
56
+ border-radius: var(--pcoi-semantic-radius-badge);
57
57
  background: var(--pcoi-semantic-bg-default);
58
58
  position: relative;
59
59
  transition: border-color var(--pcoi-effect-transition-fast, 0.2s ease);
@@ -65,9 +65,9 @@
65
65
  top: 50%;
66
66
  left: 50%;
67
67
  transform: translate(-50%, -50%);
68
- width: var(--pcoi-layout-component-radio-dot);
69
- height: var(--pcoi-layout-component-radio-dot);
70
- border-radius: var(--pcoi-radius-full);
68
+ width: var(--pcoi-semantic-sizing-radio-dot-size);
69
+ height: var(--pcoi-semantic-sizing-radio-dot-size);
70
+ border-radius: var(--pcoi-semantic-radius-badge);
71
71
  background: var(--pcoi-semantic-action-primary-bg);
72
72
  opacity: 0;
73
73
  transition: opacity var(--pcoi-effect-transition-fast, 0.2s ease);
@@ -2,8 +2,8 @@
2
2
 
3
3
  .pcoi-section-header {
4
4
  text-align: center;
5
- max-width: var(--pcoi-layout-container-narrow);
6
- margin: 0 auto var(--pcoi-spacing-64);
5
+ max-width: var(--pcoi-semantic-sizing-container-narrow);
6
+ margin: 0 auto var(--pcoi-semantic-spacing-section-header-margin);
7
7
  }
8
8
 
9
9
  .pcoi-section-header__label {
@@ -14,7 +14,7 @@
14
14
  letter-spacing: var(--pcoi-semantic-type-section-label-letter-spacing);
15
15
  text-transform: uppercase;
16
16
  color: var(--pcoi-semantic-text-accent);
17
- margin-bottom: var(--pcoi-spacing-16);
17
+ margin-bottom: var(--pcoi-semantic-spacing-stack-md);
18
18
  }
19
19
 
20
20
  .pcoi-section-header__title {
@@ -23,7 +23,7 @@
23
23
  line-height: var(--pcoi-semantic-type-heading-line-height);
24
24
  letter-spacing: var(--pcoi-semantic-type-heading-letter-spacing);
25
25
  color: var(--pcoi-semantic-text-primary);
26
- margin: 0 0 var(--pcoi-spacing-16) 0;
26
+ margin: 0 0 var(--pcoi-semantic-spacing-stack-md) 0;
27
27
  }
28
28
 
29
29
  .pcoi-section-header__title em {
@@ -0,0 +1,23 @@
1
+ import figma from "@figma/code-connect";
2
+ import { SectionHeader } from "./SectionHeader";
3
+
4
+ /**
5
+ * Code Connect: SectionHeader
6
+ * Maps Figma section header properties to React SectionHeader props
7
+ */
8
+ figma.connect(SectionHeader, "https://www.figma.com/file/PCOIxDesignSystem/PCOI-Design-System?node-id=106-1", {
9
+ props: {
10
+ label: figma.string("Label"),
11
+ title: figma.string("Title"),
12
+ titleEmphasis: figma.string("Title Emphasis"),
13
+ description: figma.string("Description"),
14
+ },
15
+ example: ({ label, title, titleEmphasis, description }) => (
16
+ <SectionHeader
17
+ label={label}
18
+ title={title}
19
+ titleEmphasis={titleEmphasis || undefined}
20
+ description={description || undefined}
21
+ />
22
+ ),
23
+ });
@@ -3,7 +3,7 @@
3
3
  .pcoi-select {
4
4
  display: flex;
5
5
  flex-direction: column;
6
- gap: var(--pcoi-spacing-6);
6
+ gap: var(--pcoi-semantic-spacing-form-gap-compact);
7
7
  }
8
8
 
9
9
  .pcoi-select__label {
@@ -16,7 +16,7 @@
16
16
 
17
17
  .pcoi-select__required {
18
18
  color: var(--pcoi-semantic-text-error);
19
- margin-left: var(--pcoi-spacing-4);
19
+ margin-left: var(--pcoi-semantic-spacing-inline-2xs);
20
20
  }
21
21
 
22
22
  .pcoi-select__wrapper {
@@ -31,8 +31,8 @@
31
31
  color: var(--pcoi-semantic-text-primary);
32
32
  background: var(--pcoi-semantic-bg-default);
33
33
  border: 1px solid var(--pcoi-semantic-border-default);
34
- border-radius: var(--pcoi-radius-sm);
35
- padding: var(--pcoi-spacing-12) var(--pcoi-spacing-36) var(--pcoi-spacing-12) var(--pcoi-spacing-14);
34
+ border-radius: var(--pcoi-semantic-radius-input);
35
+ padding: var(--pcoi-semantic-spacing-input-y) var(--pcoi-semantic-spacing-btn-x-lg) var(--pcoi-semantic-spacing-input-y) var(--pcoi-semantic-spacing-input-x-compact);
36
36
  cursor: pointer;
37
37
  transition: border-color var(--pcoi-effect-transition-fast, 0.2s ease),
38
38
  box-shadow var(--pcoi-effect-transition-fast, 0.2s ease);
@@ -59,7 +59,7 @@
59
59
 
60
60
  .pcoi-select__chevron {
61
61
  position: absolute;
62
- right: var(--pcoi-spacing-14);
62
+ right: var(--pcoi-semantic-spacing-input-x-compact);
63
63
  top: 50%;
64
64
  transform: translateY(-50%);
65
65
  pointer-events: none;
@@ -0,0 +1,33 @@
1
+ import figma from "@figma/code-connect";
2
+ import { Select } from "./Select";
3
+
4
+ const options = [
5
+ { value: "one", label: "Option One" },
6
+ { value: "two", label: "Option Two" },
7
+ { value: "three", label: "Option Three" },
8
+ ];
9
+
10
+ /**
11
+ * Code Connect: Select
12
+ * Maps Figma select field states to React Select props
13
+ */
14
+ figma.connect(Select, "https://www.figma.com/file/PCOIxDesignSystem/PCOI-Design-System?node-id=109-1", {
15
+ props: {
16
+ label: figma.string("Label"),
17
+ placeholder: figma.string("Placeholder"),
18
+ required: figma.boolean("Required"),
19
+ disabled: figma.boolean("Disabled"),
20
+ hasError: figma.boolean("Error"),
21
+ },
22
+ example: ({ label, placeholder, required, disabled, hasError }) => (
23
+ <Select
24
+ name="mapped-select"
25
+ label={label}
26
+ options={options}
27
+ placeholder={placeholder || undefined}
28
+ required={required}
29
+ disabled={disabled}
30
+ error={hasError ? "Please select an option." : undefined}
31
+ />
32
+ ),
33
+ });
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { ChevronDownIcon } from "../../../icons/src/react/ChevronDownIcon";
2
+ import { ChevronDownIcon } from "@pcoi/icons";
3
3
  import type { OptionItem } from "../types";
4
4
 
5
5
  export interface SelectProps
@@ -3,15 +3,15 @@
3
3
  .pcoi-signals {
4
4
  background: var(--pcoi-semantic-bg-surface);
5
5
  border: 1px solid var(--pcoi-semantic-border-default);
6
- border-radius: var(--pcoi-radius-md);
7
- padding: var(--pcoi-spacing-40) var(--pcoi-spacing-48);
6
+ border-radius: var(--pcoi-semantic-radius-card);
7
+ padding: var(--pcoi-semantic-spacing-component-gap) var(--pcoi-semantic-spacing-inline-xl);
8
8
  }
9
9
 
10
10
  .pcoi-signals__title {
11
11
  font-size: var(--pcoi-semantic-type-heading-sm-size);
12
12
  font-weight: var(--pcoi-semantic-type-emphasis-weight);
13
13
  color: var(--pcoi-semantic-text-accent);
14
- margin: 0 0 var(--pcoi-spacing-24) 0;
14
+ margin: 0 0 var(--pcoi-semantic-spacing-panel-padding) 0;
15
15
  }
16
16
 
17
17
  .pcoi-signals__list {
@@ -20,12 +20,12 @@
20
20
  margin: 0;
21
21
  display: flex;
22
22
  flex-direction: column;
23
- gap: var(--pcoi-spacing-14);
23
+ gap: var(--pcoi-semantic-spacing-stack-compact);
24
24
  }
25
25
 
26
26
  .pcoi-signals__item {
27
27
  position: relative;
28
- padding-left: var(--pcoi-spacing-24);
28
+ padding-left: var(--pcoi-semantic-spacing-panel-padding);
29
29
  font-size: var(--pcoi-semantic-type-body-compact-size);
30
30
  color: var(--pcoi-semantic-text-secondary);
31
31
  line-height: var(--pcoi-semantic-type-body-compact-line-height);
@@ -36,9 +36,9 @@
36
36
  position: absolute;
37
37
  left: 0;
38
38
  top: 8px;
39
- width: var(--pcoi-layout-component-bullet);
40
- height: var(--pcoi-layout-component-bullet);
41
- border-radius: var(--pcoi-radius-full);
39
+ width: var(--pcoi-semantic-sizing-bullet-size);
40
+ height: var(--pcoi-semantic-sizing-bullet-size);
41
+ border-radius: var(--pcoi-semantic-radius-badge);
42
42
  background: var(--pcoi-semantic-text-accent);
43
43
  opacity: 0.5;
44
44
  }
@@ -46,6 +46,6 @@
46
46
  /* ── Responsive ── */
47
47
  @media (max-width: 768px) {
48
48
  .pcoi-signals {
49
- padding: var(--pcoi-spacing-24) var(--pcoi-spacing-28);
49
+ padding: var(--pcoi-semantic-spacing-panel-padding) var(--pcoi-semantic-spacing-inline-lg);
50
50
  }
51
51
  }
@@ -3,17 +3,17 @@
3
3
  .pcoi-suggestion-card {
4
4
  display: flex;
5
5
  align-items: center;
6
- gap: var(--pcoi-spacing-12);
6
+ gap: var(--pcoi-semantic-spacing-panel-gap);
7
7
  width: 100%;
8
- padding: var(--pcoi-spacing-16) var(--pcoi-spacing-20);
8
+ padding: var(--pcoi-semantic-spacing-panel-padding-sm) var(--pcoi-semantic-spacing-card-gap);
9
9
  font-family: var(--pcoi-semantic-type-body-font);
10
10
  font-size: var(--pcoi-semantic-type-body-size);
11
11
  line-height: var(--pcoi-semantic-type-body-line-height);
12
12
  color: var(--pcoi-semantic-text-secondary);
13
13
  text-align: left;
14
- background: var(--pcoi-color-bg-card);
14
+ background: var(--pcoi-semantic-bg-card);
15
15
  border: 1px solid var(--pcoi-semantic-border-card);
16
- border-radius: var(--pcoi-radius-md);
16
+ border-radius: var(--pcoi-semantic-radius-card);
17
17
  cursor: pointer;
18
18
  transition:
19
19
  background var(--pcoi-effect-transition-medium),
@@ -23,7 +23,7 @@
23
23
  }
24
24
 
25
25
  .pcoi-suggestion-card:hover {
26
- background: var(--pcoi-color-bg-card-hover);
26
+ background: var(--pcoi-semantic-bg-card-hover);
27
27
  border-color: var(--pcoi-semantic-border-card-hover);
28
28
  transform: var(--pcoi-effect-transform-hover-lift-sm);
29
29
  }
@@ -3,7 +3,7 @@
3
3
  .pcoi-suggestion-cards {
4
4
  display: flex;
5
5
  flex-direction: column;
6
- gap: var(--pcoi-spacing-16);
6
+ gap: var(--pcoi-semantic-spacing-stack-md);
7
7
  }
8
8
 
9
9
  /* ── Responsive: 3-col grid on tablet and up ── */