@canonical/code-standards 0.1.0 → 0.1.2

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/docs/react.md CHANGED
@@ -8,7 +8,7 @@ The index.ts file must be a complete barrel export for the component folder, re-
8
8
 
9
9
  ### Do
10
10
 
11
- (Do) Create an index.ts file that re-exports all public APIs from the component folder.
11
+ Create an index.ts file that re-exports all public APIs from the component folder.
12
12
  ```typescript
13
13
  // index.ts
14
14
  export { default as [MyComponent] } from './[MyComponent].js';
@@ -19,13 +19,13 @@ export { default as SubComponent } from './SubComponent.js';
19
19
 
20
20
  ### Don't
21
21
 
22
- (Don't) Omit the index.ts file or fail to re-export all public APIs.
22
+ Omit the index.ts file or fail to re-export all public APIs.
23
23
  ```typescript
24
24
  // Bad: index.ts only exports default, omits types and named exports
25
25
  export { default } from './[MyComponent].js';
26
26
  ```
27
27
 
28
- (Don't) Use `export * from './types.js'` as it allows value exports, which is not expected for types files.
28
+ Use `export * from './types.js'` as it allows value exports, which is not expected for types files.
29
29
  ```typescript
30
30
  // Bad: Using export * from './types.js' allows value exports, which is not allowed
31
31
  export * from './types.js';
@@ -47,7 +47,7 @@ Component CSS class names must be constructed following a specific pattern:
47
47
 
48
48
  ### Do
49
49
 
50
- (Do) Follow the complete pattern for class name construction.
50
+ Follow the complete pattern for class name construction.
51
51
  ```tsx
52
52
  const componentCssClassName = "ds badge";
53
53
 
@@ -72,19 +72,19 @@ const Badge = ({
72
72
 
73
73
  ### Don't
74
74
 
75
- (Don't) Hardcode the base class name inside the JSX.
75
+ Hardcode the base class name inside the JSX.
76
76
  ```tsx
77
77
  // Bad: Base class "ds badge" is hardcoded.
78
78
  <span className={["ds badge", severity, className].filter(Boolean).join(" ")}>
79
79
  ```
80
80
 
81
- (Don't) Place the consumer `className` prop before other classes.
81
+ Place the consumer `className` prop before other classes.
82
82
  ```tsx
83
83
  // Bad: Consumer class is first
84
84
  <span className={[className, componentCssClassName, severity].filter(Boolean).join(" ")}>
85
85
  ```
86
86
 
87
- (Don't) Use string concatenation or template literals to add class names.
87
+ Use string concatenation or template literals to add class names.
88
88
  ```tsx
89
89
  // Bad: Harder to read and maintain, vulnerable to inconsistent formatting
90
90
  <span className={`${componentCssClassName} ${severity} ${className}`}>
@@ -101,7 +101,7 @@ Component dependencies must follow a strict unidirectional flow:
101
101
 
102
102
  ### Do
103
103
 
104
- (Do) Place subcomponents in a `common/` folder inside the parent component directory.
104
+ Place subcomponents in a `common/` folder inside the parent component directory.
105
105
  ```
106
106
  Card/
107
107
  ├── Card.tsx
@@ -115,7 +115,7 @@ Card/
115
115
  └── index.ts
116
116
  ```
117
117
 
118
- (Do) Allow subcomponents to depend on siblings or shared utilities within the same component scope.
118
+ Allow subcomponents to depend on siblings or shared utilities within the same component scope.
119
119
  ```typescript
120
120
  // Header.tsx can import from utils/
121
121
  import { helper } from '../utils/helpers.js';
@@ -126,13 +126,13 @@ import Header from '../Header.js';
126
126
 
127
127
  ### Don't
128
128
 
129
- (Don't) Create dependencies that flow upwards from a subcomponent to its parent.
129
+ Create dependencies that flow upwards from a subcomponent to its parent.
130
130
  ```typescript
131
131
  // Bad: Header.tsx in Card/common/ should not import from Card.tsx
132
132
  import Card from '../../Card.js';
133
133
  ```
134
134
 
135
- (Don't) Allow external components to depend on the internal structure of another component.
135
+ Allow external components to depend on the internal structure of another component.
136
136
  ```typescript
137
137
  // Bad: AnotherComponent should not import from Card's internal common folder
138
138
  import Header from '../Card/common/Header.js';
@@ -146,7 +146,7 @@ Component folder and file naming is based on scope. Component-specific files (im
146
146
 
147
147
  ### Do
148
148
 
149
- (Do) Prefix component-specific files and use generic names for domain-level files.
149
+ Prefix component-specific files and use generic names for domain-level files.
150
150
  ```
151
151
  [MyComponent]/
152
152
  ├── [MyComponent].tsx # Component-specific
@@ -159,7 +159,7 @@ Component folder and file naming is based on scope. Component-specific files (im
159
159
 
160
160
  ### Don't
161
161
 
162
- (Don't) Add redundant prefixes to domain-level files.
162
+ Add redundant prefixes to domain-level files.
163
163
  ```
164
164
  [MyComponent]/
165
165
  ├── [MyComponent].Context.tsx # Bad: Redundant prefix
@@ -171,18 +171,21 @@ Component folder and file naming is based on scope. Component-specific files (im
171
171
 
172
172
  ## react/component/naming
173
173
 
174
- Components must use PascalCase naming and be descriptive of their purpose.
174
+ Use PascalCase and descriptive names for components:
175
+ UserProfile
176
+ NavigationBar
177
+ SearchResultList
175
178
 
176
179
  ### Do
177
180
 
178
- (Do) Use PascalCase and descriptive names for components:
181
+ Use PascalCase and descriptive names for components:
179
182
  UserProfile
180
183
  NavigationBar
181
184
  SearchResultList
182
185
 
183
186
  ### Don't
184
187
 
185
- (Don't) Use non-PascalCase or unclear names:
188
+ Use non-PascalCase or unclear names:
186
189
  userProfile
187
190
  navigation_bar
188
191
  searchresultlist
@@ -199,7 +202,7 @@ Component props must:
199
202
 
200
203
  ### Do
201
204
 
202
- (Do) Document props with TSDoc comments and use proper destructuring and spreading.
205
+ Document props with TSDoc comments and use proper destructuring and spreading.
203
206
  ```typescript
204
207
  interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
205
208
  /** The button's text content */
@@ -229,7 +232,7 @@ const Button = ({
229
232
 
230
233
  ### Don't
231
234
 
232
- (Don't) Mix explicit and spread props or destructure props unnecessarily.
235
+ Mix explicit and spread props or destructure props unnecessarily.
233
236
  ```typescript
234
237
  // Bad: Mixing explicit props with spread
235
238
  const Button = (props: ButtonProps) => (
@@ -271,7 +274,7 @@ Components that render HTML markup must extend the base HTML element props inter
271
274
 
272
275
  ### Do
273
276
 
274
- (Do) Extend the appropriate React HTML props interface and add component-specific props.
277
+ Extend the appropriate React HTML props interface and add component-specific props.
275
278
  ```typescript
276
279
  export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
277
280
  /** The button label */
@@ -281,7 +284,7 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
281
284
 
282
285
  ### Don't
283
286
 
284
- (Don't) Manually redefine standard HTML attributes that are already available through the base interface.
287
+ Manually redefine standard HTML attributes that are already available through the base interface.
285
288
  ```typescript
286
289
  export interface ButtonProps {
287
290
  /** The button label */
@@ -299,7 +302,7 @@ Wrapper components must use namespaced props for inner components and accept uns
299
302
 
300
303
  ### Do
301
304
 
302
- (Do) Use namespaced props for inner components and unscoped props for the wrapper element.
305
+ Use namespaced props for inner components and unscoped props for the wrapper element.
303
306
  ```tsx
304
307
  interface ThumbnailSectionProps extends SectionProps {
305
308
  /** Props for the thumbnail image */
@@ -321,7 +324,7 @@ const ThumbnailSection = ({
321
324
 
322
325
  ### Don't
323
326
 
324
- (Don't) Mix prop scopes between wrapper and inner components.
327
+ Mix prop scopes between wrapper and inner components.
325
328
  ```tsx
326
329
  interface ThumbnailSectionProps {
327
330
  src: string; // Bad: Unscoped image props
@@ -344,7 +347,7 @@ Context providers must use `Provider.tsx` as the main component file instead of
344
347
 
345
348
  ### Do
346
349
 
347
- (Do) Organize provider-related files by separating concerns into distinct files: place the context definition in `Context.tsx`, the provider implementation in `Provider.tsx`, and provider-specific hooks in a `hooks/` directory. Create a `hooks/useProviderState.ts` file to centrally manage the state of the provider.`
350
+ Organize provider-related files by separating concerns into distinct files: place the context definition in `Context.tsx`, the provider implementation in `Provider.tsx`, and provider-specific hooks in a `hooks/` directory. Create a `hooks/useProviderState.ts` file to centrally manage the state of the provider.`
348
351
  ```
349
352
  [MyComponent]/
350
353
  ├── Context.tsx
@@ -361,8 +364,8 @@ Context providers must use `Provider.tsx` as the main component file instead of
361
364
  ├── types.ts
362
365
  └── useProviderState.ts
363
366
  ```
364
- (Do) Create the context type within `types.ts`.
365
367
 
368
+ Create the context type within `types.ts`.
366
369
  ```typescript
367
370
  // [MyComponent]/types.ts
368
371
  /** The value of the config context */
@@ -374,8 +377,7 @@ export interface ContextOptions {
374
377
  }
375
378
  ```
376
379
 
377
- (Do) create the provider props type within `types.ts`, accepting `children` at a minimum and more props as needed.
378
-
380
+ create the provider props type within `types.ts`, accepting `children` at a minimum and more props as needed.
379
381
  ```typescript
380
382
  // [MyComponent]/types.ts
381
383
 
@@ -386,7 +388,7 @@ export interface ProviderProps {
386
388
  }
387
389
  ```
388
390
 
389
- (Do) Create a `Context.tsx` file for the context definition.
391
+ Create a `Context.tsx` file for the context definition.
390
392
  ```tsx
391
393
  // [MyComponent]/Context.tsx
392
394
  import { createContext } from "react";
@@ -397,8 +399,7 @@ const Context = createContext<ContextOptions | undefined>(undefined);
397
399
  export default Context;
398
400
  ```
399
401
 
400
-
401
- (Do) Use `Provider.tsx` as the main component file. The provider is responsible for wrapping children with the context.
402
+ Use `Provider.tsx` as the main component file. The provider is responsible for wrapping children with the context.
402
403
  ```tsx
403
404
  // [MyComponent]/Provider.tsx
404
405
  import Context from "./Context.js";
@@ -413,7 +414,7 @@ const Provider = ({ children }: ProviderProps) => {
413
414
  export default Provider;
414
415
  ```
415
416
 
416
- (Do) Use `index.ts` to export the `Provider` as a named component that matches the folder name, casting it to the component type.
417
+ Use `index.ts` to export the `Provider` as a named component that matches the folder name, casting it to the component type.
417
418
  ```typescript
418
419
  // [MyComponent]/types.ts
419
420
  import type { ReactElement } from "react";
@@ -430,7 +431,7 @@ export const [MyComponent] = Provider as [MyComponent]Component;
430
431
  export default [MyComponent];
431
432
  ```
432
433
 
433
- (Do) Create provider state hook types in `hooks/types.ts` for context providers.
434
+ Create provider state hook types in `hooks/types.ts` for context providers.
434
435
  ```typescript
435
436
  // [MyComponent]/hooks/types.ts
436
437
  import type { ContextOptions, ProviderProps } from "../types.js";
@@ -442,7 +443,7 @@ export type UseProviderStateProps = Omit<ProviderProps, "children">;
442
443
  export type UseProviderStateResult = ContextOptions;
443
444
  ```
444
445
 
445
- (Do) Create a provider state hook implementation.
446
+ Create a provider state hook implementation.
446
447
  ```tsx
447
448
  // [MyComponent]/hooks/useProviderState.ts
448
449
  import { useContext } from "react";
@@ -460,14 +461,14 @@ const useProviderState = ({
460
461
 
461
462
  ### Don't
462
463
 
463
- (Don't) Create a separate component file (e.g., `[MyComponent].tsx`) when `Provider.tsx` exists. The provider is the main component.
464
+ Create a separate component file (e.g., `[MyComponent].tsx`) when `Provider.tsx` exists. The provider is the main component.
464
465
  ```
465
466
  [MyComponent]/
466
467
  ├── [MyComponent].tsx
467
468
  └── Provider.tsx
468
469
  ```
469
470
 
470
- (Don't) Nest context-related files in a `context/` subfolder.
471
+ Nest context-related files in a `context/` subfolder.
471
472
  ```
472
473
  [MyComponent]/
473
474
  └── context/ # Unnecessary nesting
@@ -475,15 +476,14 @@ const useProviderState = ({
475
476
  └── Provider.tsx
476
477
  ```
477
478
 
478
- (Don't) Create multiple provider files within the same component folder.
479
+ Create multiple provider files within the same component folder.
479
480
  ```
480
481
  [MyComponent]/
481
482
  ├── Provider.tsx
482
483
  └── AnotherProvider.tsx # Only one provider per component
483
484
  ```
484
485
 
485
- (Don't) Mix concerns of the Provider and its state.
486
-
486
+ Mix concerns of the Provider and its state.
487
487
  ```tsx
488
488
  exort const Provider = ({ children }: ProviderProps) => {
489
489
  const [state, setState] = useState(...); // Bad: State logic mixed in
@@ -499,7 +499,7 @@ Each component must reside in its own folder, which contains all related files:
499
499
 
500
500
  ### Do
501
501
 
502
- (Do) Place all component-related files within a single folder named after the component.
502
+ Place all component-related files within a single folder named after the component.
503
503
  ```bash
504
504
  [MyComponent]/
505
505
  ├── [MyComponent].tsx
@@ -512,7 +512,7 @@ Each component must reside in its own folder, which contains all related files:
512
512
 
513
513
  ### Don't
514
514
 
515
- (Don't) Scatter component files across different parts of the application.
515
+ Scatter component files across different parts of the application.
516
516
  ```bash
517
517
  # Bad: Files are not co-located
518
518
  components/
@@ -540,7 +540,7 @@ Private subcomponents must remain internal to the component's implementation and
540
540
 
541
541
  ### Do
542
542
 
543
- (Do) Export public subcomponents by attaching them to the parent component using dot notation:
543
+ Export public subcomponents by attaching them to the parent component using dot notation
544
544
  ```typescript
545
545
  const Item = (props: ItemProps) => { /* ... */ };
546
546
  const Accordion = (props: AccordionProps) => { /* ... */ };
@@ -548,14 +548,14 @@ Accordion.Item = Item;
548
548
  export default Accordion;
549
549
  ```
550
550
 
551
- (Do) Use semantic, self-descriptive names for subcomponents:
551
+ Use semantic, self-descriptive names for subcomponents
552
552
  ```typescript
553
553
  Accordion.Item
554
554
  Card.Header
555
555
  Card.Footer
556
556
  ```
557
557
 
558
- (Do) Keep subcomponent nesting to a single level:
558
+ Keep subcomponent nesting to a single level
559
559
  ```typescript
560
560
  <Card>
561
561
  <Card.Header />
@@ -565,17 +565,17 @@ Card.Footer
565
565
 
566
566
  ### Don't
567
567
 
568
- (Don't) Repeat the parent component name in subcomponent names:
568
+ Repeat the parent component name in subcomponent names
569
569
  ```typescript
570
570
  Card.CardHeader = Header; // Bad: Redundant 'Card' prefix
571
571
  ```
572
572
 
573
- (Don't) Map a subcomponent to a different name (renaming):
573
+ Map a subcomponent to a different name (renaming)
574
574
  ```typescript
575
575
  Card.Top = Header; // Bad: Mapping 'Header' to 'Top' is not allowed
576
576
  ```
577
577
 
578
- (Don't) Use non-semantic or unclear subcomponent names:
578
+ Use non-semantic or unclear subcomponent names
579
579
  ```
580
580
  Card/
581
581
  └── common/
@@ -583,7 +583,7 @@ Card/
583
583
  ├── Element/ # Bad: Too vague, not semantic - what element?
584
584
  ```
585
585
 
586
- (Don't) Nest subcomponents more than one level deep:
586
+ Nest subcomponents more than one level deep
587
587
  ```typescript
588
588
  <Card>
589
589
  <Card.Header>
@@ -592,7 +592,7 @@ Card/
592
592
  </Card>
593
593
  ```
594
594
 
595
- (Don't) Export private subcomponents that are not intended for public use.
595
+ Export private subcomponents that are not intended for public use.
596
596
  ```typescript
597
597
  // Bad: Exporting internal-only subcomponents
598
598
  export { InternalHelper };
@@ -606,7 +606,7 @@ Component TSDoc documentation must use the description from the design system on
606
606
 
607
607
  ### Do
608
608
 
609
- (Do) Use the description from the DSL ontology verbatim.
609
+ Use the description from the DSL ontology verbatim.
610
610
  ```typescript
611
611
  /**
612
612
  * The label component is a compact, non-interactive visual element used to
@@ -625,7 +625,7 @@ const Label = ({ children, criticality, className, ...props }: LabelProps) => (
625
625
 
626
626
  ### Don't
627
627
 
628
- (Don't) Write custom descriptions that deviate from the DSL.
628
+ Write custom descriptions that deviate from the DSL.
629
629
  ```typescript
630
630
  // Bad: Custom title and paraphrased description
631
631
  /**
@@ -635,21 +635,19 @@ const Label = ({ children, criticality, className, ...props }: LabelProps) => (
635
635
  */
636
636
  ```
637
637
 
638
- (Don't) Include @example blocks - stories fulfill this role.
638
+ Include @example blocks - stories fulfill this role.
639
639
  ```typescript
640
640
  // Bad: Examples belong in stories, not TSDoc
641
641
  /**
642
642
  * The label component is a compact...
643
643
  *
644
644
  * @example
645
- * ```tsx
646
645
  * <Label>Default</Label>
647
646
  * <Label criticality="warning">Warning</Label>
648
- * ```
649
647
  */
650
648
  ```
651
649
 
652
- (Don't) Omit the @implements tag that links to the DSL.
650
+ Omit the @implements tag that links to the DSL.
653
651
  ```typescript
654
652
  // Bad: Missing @implements tag
655
653
  /**
@@ -672,7 +670,7 @@ Each hook must define a [HookName]Props and [HookName]Result type in `hooks/type
672
670
 
673
671
  ### Do
674
672
 
675
- (Do) Create a custom hook that focuses on a single concern within the domain.
673
+ Create a custom hook that focuses on a single concern within the domain.
676
674
  ```typescript
677
675
  // [MyComponent]/hooks/useWindowFitment.ts
678
676
  const useWindowFitment = ({
@@ -680,7 +678,8 @@ const useWindowFitment = ({
680
678
  autoFit = false,
681
679
  }: UseWindowFitmentProps): UseWindowFitmentResult => {
682
680
  ```
683
- (Do) Create hook types in `hooks/types.ts`.
681
+
682
+ Create hook types in `hooks/types.ts`.
684
683
  ```typescript
685
684
  // [MyComponent]/hooks/types.ts
686
685
  export interface UseWindowFitmentProps {
@@ -718,7 +717,7 @@ export interface UseWindowFitmentResult {
718
717
 
719
718
  ### Don't
720
719
 
721
- (Don't) Create a custom hook for simple, non-reusable state.
720
+ Create a custom hook for simple, non-reusable state.
722
721
  ```tsx
723
722
  // Bad: Unnecessary abstraction for a simple counter
724
723
  const useCounter = () => {
@@ -727,7 +726,7 @@ const useCounter = () => {
727
726
  };
728
727
  ```
729
728
 
730
- (Don't) Mix multiple concerns in a single hook
729
+ Mix multiple concerns in a single hook
731
730
  ```typescript
732
731
  // Bad: Multiple concerns in one hook
733
732
  const useUserData = () => {
@@ -747,7 +746,7 @@ The hook name must start with 'use' and clearly describe its purpose.
747
746
 
748
747
  ### Do
749
748
 
750
- (Do) Name custom hooks so the hook name starts with 'use' and is descriptive:
749
+ Name custom hooks so the hook name starts with 'use' and is descriptive
751
750
  ```typescript
752
751
  useWindowSize()
753
752
  useAuthentication()
@@ -756,7 +755,7 @@ useFormValidation()
756
755
 
757
756
  ### Don't
758
757
 
759
- (Don't) Name hooks without the 'use' prefix at the start of the hook name:
758
+ Name hooks without the 'use' prefix at the start of the hook name
760
759
  ```typescript
761
760
  windowSize()
762
761
  getAuth()