Skip to content
Draft

18/19 #3290

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ module.exports = {

ignorePatterns: ['packages/code-connect/**/*'],

settings: {
'import/parsers': {
[require.resolve('@typescript-eslint/parser')]: [
'.ts',
'.cts',
'.mts',
'.tsx',
'.js',
'.jsx',
'.mjs',
'.cjs',
],
},
},

rules: {
'gamut/prefer-themed': 'error',
'gamut/no-css-standalone': 'error',
Expand Down Expand Up @@ -38,6 +53,13 @@ module.exports = {
// being applied to subsequent plugin imports/extensions. Wild.
files: ['*.tsx', '*.ts'],
rules: {
'@typescript-eslint/no-empty-object-type': [
'error',
{
allowInterfaces: 'with-single-extends',
allowObjectTypes: 'always',
},
],
'no-void': ['error', { allowAsStatement: true }],
// These rules could be useful, but we haven't gotten around to enabling them here
// See WEB-2 for general tracking.
Expand Down
12 changes: 12 additions & 0 deletions .nx/version-plans/version-plan-1775236316343.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
eslint-plugin-gamut: major
gamut-illustrations: major
gamut-patterns: major
gamut-styles: major
gamut-icons: major
gamut-tests: major
variance: major
gamut: major
---

React 18+19
3 changes: 2 additions & 1 deletion jest.config.base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Config } from 'jest';
import path from 'node:path';

import type { Config } from 'jest';

const COVERAGE_PATH_IGNORE_PATTERNS = [
'<rootDir>/node_modules/',
'<rootDir>/dist/',
Expand Down
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@babel/preset-env": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"@babel/preset-typescript": "^7.24.7",
"@codecademy/eslint-config": "8.0.0",
"@codecademy/eslint-config": "8.2.0",
"@codecademy/prettier-config": "^0.2.0",
"@codecademy/tsconfig": "^0.3.0",
"@commander-js/extra-typings": "^14.0.0",
Expand Down Expand Up @@ -51,18 +51,18 @@
"@types/invariant": "2.2.29",
"@types/konami-code-js": "^0.8.0",
"@types/lodash": "4.17.23",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@types/react-test-renderer": "^19.0.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react-test-renderer": "^18.2.0",
"@types/stylis": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"@typescript-eslint/eslint-plugin": "^8.57.0",
"@typescript-eslint/parser": "^8.57.0",
"babel-jest": "29.6.4",
"babel-plugin-macros": "3.0.1",
"babel-plugin-macros": "^3.0.1",
"commander": "^14.0.3",
"component-test-setup": "^0.3.1",
"cpy-cli": "^4.1.0",
"eslint": "^8.11.0",
"eslint": "^8.57.0",
"eslint-plugin-gamut": "workspace:*",
"eslint-plugin-local-rules": "^1.1.0",
"eslint-plugin-lodash": "^7.4.0",
Expand Down Expand Up @@ -113,13 +113,14 @@
"repository": "git@github.com:Codecademy/gamut.git",
"resolutions": {
"@react-aria/interactions": "3.25.0",
"@types/react-dom": "^19.0.0",
"@types/react": "^19.0.0",
"@typescript-eslint/utils": "^5.15.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"@types/react-test-renderer": "^18.2.0",
"@typescript-eslint/utils": "^8.57.0",
"axios": "1.14.0",
"error-ex": "1.3.4",
"react-dom": "^19.0.0",
"react": "^19.0.0"
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"scripts": {
"build": "nx run-many --target=build --all",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-gamut/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "2.4.3",
"author": "Codecademy Engineering <dev@codecademy.com>",
"dependencies": {
"@typescript-eslint/utils": "^5.15.0"
"@typescript-eslint/utils": "^8.57.0"
},
"files": [
"dist"
Expand Down
1 change: 0 additions & 1 deletion packages/eslint-plugin-gamut/src/gamut-import-paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ export default createRule({
meta: {
docs: {
description: 'Ensure Gamut import statements have proper module paths.',
recommended: 'error',
},
fixable: 'code',
messages: {
Expand Down
6 changes: 3 additions & 3 deletions packages/eslint-plugin-gamut/src/no-css-standalone.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ESLintUtils } from '@typescript-eslint/utils';
import { TSESLint } from '@typescript-eslint/utils';

import rule from './no-css-standalone';

const ruleTester = new ESLintUtils.RuleTester({
parser: '@typescript-eslint/parser',
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
});

ruleTester.run('no-css-standalone', rule, {
Expand Down
1 change: 0 additions & 1 deletion packages/eslint-plugin-gamut/src/no-css-standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default createRule({
meta: {
docs: {
description: 'Ensure no standalone .css or .scss files.',
recommended: 'error',
},
messages: {
noCssStandalone:
Expand Down
6 changes: 3 additions & 3 deletions packages/eslint-plugin-gamut/src/no-inline-style.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ESLintUtils } from '@typescript-eslint/utils';
import { TSESLint } from '@typescript-eslint/utils';

import rule from './no-inline-style';

const ruleTester = new ESLintUtils.RuleTester({
parser: '@typescript-eslint/parser',
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaFeatures: {
jsx: true,
Expand Down
8 changes: 6 additions & 2 deletions packages/eslint-plugin-gamut/src/no-inline-style.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { AST_NODE_TYPES } from '@typescript-eslint/utils';

import { createRule } from './createRule';

export default createRule({
create(context) {
return {
JSXAttribute(node) {
if (node.name.type === 'JSXIdentifier' && node.name.name === 'style') {
if (
node.name.type === AST_NODE_TYPES.JSXIdentifier &&
node.name.name === 'style'
) {
context.report({
messageId: 'noInlineStyle',
node,
Expand All @@ -17,7 +22,6 @@ export default createRule({
meta: {
docs: {
description: 'Disallow inline style props on JSX elements.',
recommended: 'error',
},
messages: {
noInlineStyle:
Expand Down
6 changes: 3 additions & 3 deletions packages/eslint-plugin-gamut/src/no-kbd-element.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ESLintUtils } from '@typescript-eslint/utils';
import { TSESLint } from '@typescript-eslint/utils';

import rule from './no-kbd-element';

const ruleTester = new ESLintUtils.RuleTester({
parser: '@typescript-eslint/parser',
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
parserOptions: {
ecmaFeatures: {
jsx: true,
Expand Down
8 changes: 6 additions & 2 deletions packages/eslint-plugin-gamut/src/no-kbd-element.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { AST_NODE_TYPES } from '@typescript-eslint/utils';

import { createRule } from './createRule';

export default createRule({
create(context) {
return {
JSXOpeningElement(node) {
if (node.name.type === 'JSXIdentifier' && node.name.name === 'kbd') {
if (
node.name.type === AST_NODE_TYPES.JSXIdentifier &&
node.name.name === 'kbd'
) {
context.report({
messageId: 'noKbdElement',
node,
Expand All @@ -18,7 +23,6 @@ export default createRule({
docs: {
description:
'Intended to be used in Storybook docs to disallow use of the `kbd` HTML element in favor of the `KeyboardKey` component for styling purposes.',
recommended: 'error',
},
messages: {
noKbdElement: 'Please use the `KeyboardKey` component instead.',
Expand Down
6 changes: 3 additions & 3 deletions packages/eslint-plugin-gamut/src/prefer-themed.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ESLintUtils } from '@typescript-eslint/utils';
import { TSESLint } from '@typescript-eslint/utils';

import rule from './prefer-themed';

const ruleTester = new ESLintUtils.RuleTester({
parser: '@typescript-eslint/parser',
const ruleTester = new TSESLint.RuleTester({
parser: require.resolve('@typescript-eslint/parser'),
});

ruleTester.run('prefer-themed', rule, {
Expand Down
12 changes: 4 additions & 8 deletions packages/eslint-plugin-gamut/src/prefer-themed.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { AST_NODE_TYPES } from '@typescript-eslint/utils';

import { createRule } from './createRule';
import {
checkArrowFuncBodyTypesAndReturnThemeVars,
Expand All @@ -10,15 +8,14 @@ export default createRule({
create(context) {
return {
TaggedTemplateExpression(node) {
if (node.tag.type === AST_NODE_TYPES.MemberExpression) {
/* eslint-disable @typescript-eslint/no-unsafe-enum-comparison -- @typescript-eslint/types use string enums; literals match ESTree */
if (node.tag.type === 'MemberExpression') {
if (node.tag.object.type !== 'Identifier') return;
const expressionVariable = node.tag.object.name;
const arrowFuncExpression = node.quasi.expressions[0];

if (
arrowFuncExpression?.type !== AST_NODE_TYPES.ArrowFunctionExpression
)
return;
if (arrowFuncExpression?.type !== 'ArrowFunctionExpression') return;
/* eslint-enable @typescript-eslint/no-unsafe-enum-comparison */

if (!isNamedVariableTheme(arrowFuncExpression)) return;

Expand Down Expand Up @@ -48,7 +45,6 @@ export default createRule({
meta: {
docs: {
description: 'Prefer themed style utility',
recommended: 'error',
},
fixable: 'code',
messages: {
Expand Down
2 changes: 1 addition & 1 deletion packages/gamut-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"author": "Codecademy Engineering <dev@codecademy.com>",
"dependencies": {
"@codecademy/gamut-styles": "17.13.1",
"component-test-setup": "*",
"component-test-setup": "^0.3.1",
"lodash": "^4.17.23"
},
"files": [
Expand Down
2 changes: 1 addition & 1 deletion packages/gamut/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"polished": "^4.1.2",
"react-aria-components": "1.7.1",
"react-focus-on": "3.10.0",
"react-hook-form": "^7.65.0",
"react-hook-form": "^7.71.2",
"react-player": "^2.16.0",
"react-select": "^5.2.2",
"react-truncate-markup": "^5.1.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const buttonPresetThemes = {
royalblue: 'brand-purple',
} as const;

const themes = [
const _themes = [
'hyper',
'navy',
'red',
Expand All @@ -39,7 +39,7 @@ const themes = [

export type ButtonDeprecatedThemes =
| keyof typeof buttonPresetThemes
| (typeof themes)[number];
| (typeof _themes)[number];

const propKeys = [
'theme',
Expand Down
10 changes: 5 additions & 5 deletions packages/gamut/src/Breadcrumbs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,24 @@ const BreadcrumbPart = styled(Box)(

export type Crumb = { title: string };

export type ClickableCrumb<T extends string | Object> = Crumb & {
export type ClickableCrumb<T extends string | object> = Crumb & {
href: string;
payload: T;
};

export type Breadcrumb<T extends string | Object> = Crumb | ClickableCrumb<T>;
export type Breadcrumb<T extends string | object> = Crumb | ClickableCrumb<T>;

export const isClickableCrumb = <T extends string | Object>(
export const isClickableCrumb = <T extends string | object>(
crumb: Breadcrumb<T>
): crumb is ClickableCrumb<T> => !!(crumb as ClickableCrumb<T>).href;

export type BreadcrumbsProps<T extends string | Object> = {
export type BreadcrumbsProps<T extends string | object> = {
crumbs: Breadcrumb<T>[];
onClick?: (event: React.MouseEvent, crumb: ClickableCrumb<T>) => void;
className?: string;
};

export const Breadcrumbs = <T extends string | Object>({
export const Breadcrumbs = <T extends string | object>({
crumbs,
onClick,
className,
Expand Down
2 changes: 2 additions & 0 deletions packages/gamut/src/Button/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ export type InlineIconButtonProps<
iconPosition?: 'right' | 'left';
};

/* eslint-disable @typescript-eslint/no-duplicate-type-constituents -- createButtonComponent yields structurally identical typeofs; union documents distinct components */
export type ButtonTypes =
| typeof CTAButton
| typeof FillButton
| typeof IconButton
| typeof StrokeButton
| typeof TextButton;
/* eslint-enable @typescript-eslint/no-duplicate-type-constituents */
6 changes: 3 additions & 3 deletions packages/gamut/src/ConnectedForm/ConnectedForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export const ConnectedForm = forwardRef(
</PropsProvider>
);
}
) as React.ForwardRefExoticComponent<
ConnectedFormProps<FormValues<Record<string, unknown>>> &
) as <Values extends FormValues<Values>>(
props: React.PropsWithoutRef<ConnectedFormProps<Values>> &
React.RefAttributes<HTMLFormElement>
>;
) => React.ReactElement;
5 changes: 3 additions & 2 deletions packages/gamut/src/ConnectedForm/ConnectedFormGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export function ConnectedFormGroup<T extends ConnectedField>({
isSoloField,
infotip,
}: ConnectedFormGroupProps<T>) {
const fieldId = typeof id === 'string' && id !== '' ? id : name;
const { error, isFirstError, isDisabled, setError, validation } = useField({
name,
disabled,
Expand All @@ -78,7 +79,7 @@ export function ConnectedFormGroup<T extends ConnectedField>({
const renderedLabel = (
<FormGroupLabel
disabled={isDisabled}
htmlFor={id || name}
htmlFor={fieldId}
infotip={infotip}
isSoloField={isSoloField}
required={!!validation?.required}
Expand All @@ -90,7 +91,7 @@ export function ConnectedFormGroup<T extends ConnectedField>({

const textError = customError || getErrorMessage(error);
const showError = !!(textError && !hideLabel);
const errorId = showError ? `${id || name}_error` : undefined;
const errorId = showError ? `${fieldId}_error` : undefined;

return (
<FormGroup spacing={hideLabel ? 'tight' : spacing}>
Expand Down
Loading
Loading