mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-02-25 14:01:29 -05:00
189 lines
9.3 KiB
Markdown
189 lines
9.3 KiB
Markdown
# Рекомендації щодо кодової бази
|
||
|
||
## Стилізація компоненту
|
||
|
||
Ми рекомендуємо стилізувати компоненти, використовуючи наш [посібник зі стилю](https://design-style-guide.freecodecamp.org/).
|
||
|
||
Кольори визначені у [`variable.css`](/client/src/components/layouts/variables.css), а шрифти визначені у [`fonts.css`](/client/src/components/layouts/fonts.css).
|
||
|
||
Ми категоричні щодо додавання нових змінних/токенів до кольорів. Після ретельного аналізу ми обрали кольори, які відповідають ідентичності бренду freeCodeCamp, досвіду розробників і доступності.
|
||
|
||
Ключове слово `!important` може бути використане для заміни значень у деяких випадках (наприклад, проблеми доступності). Ви повинні додати коментар з описом проблеми, щоб її не видалили у майбутньому рефакторингу.
|
||
|
||
### Підтримка RTL
|
||
|
||
Ми прагнемо підтримувати макети справа наліво (RTL) у кодовій базі для мов, які використовують письмо в такому напрямку. Для цього потрібно бути уважними щодо стилізації компонентів. Ось деякі правила, яких потрібно дотримуватись:
|
||
|
||
- Не використовуйте властивості `float`
|
||
- Натомість використовуйте макети Flexbox та Grid, оскільки вони мають вбудовану підтримку RTL, і їх буде простіше підтримувати та переглядати.
|
||
- Не визначайте напрямок, використовуючи `margin` та `padding`: використання `padding-right` та `margin-left` може здаватись безобідним, але ці напрямки не відображаються, коли макет змінюється на RTL, а додавання протилежних значень ускладнює утримання кодової бази.
|
||
- Використовуйте логічні властивості: ви можете додати однаковий інтервал, використовуючи `padding-inline-end` та `margin-inline-start`, і вам не доведеться переживати про макет RTL, оскільки вони відповідають тому, де починається та закінчується рядок. Крім того, вам не доведеться турбуватися про додавання додаткових значень до файлів RTL, і, відповідно, не доведеться пам’ятати про зміну однакових значень у двох файлах.
|
||
- Не використовуйте `!important` у `font-family`: для макетів RTL та LTR використовуються різні шрифти; додавання `!important` до властивості `font-family` також впливає на макет RTL.
|
||
|
||
## Загальний JavaScript
|
||
|
||
У більшості випадків наш [лінтер](how-to-setup-freecodecamp-locally.md#follow-these-steps-to-get-your-development-environment-ready) попереджатиме про будь-яке форматування, яке суперечить вподобанням нашої кодової бази.
|
||
|
||
Рекомендовано використовувати функціональні компоненти, а не класові.
|
||
|
||
## Особливий TypeScript
|
||
|
||
### Перенесення файлу JavaScript до TypeScript
|
||
|
||
#### Збереження історії файлів Git
|
||
|
||
Іноді зміна файлу з `<filename>.js` на `<filename>.ts` (або `.tsx`) призводить до видалення вихідного файлу та створення нового, а в іншому випадку просто змінюється назва файлу, згідно з Git. В ідеалі ми хочемо, щоб історія файлів була збережена.
|
||
|
||
Для цього потрібно:
|
||
|
||
1. Перейменувати файл
|
||
2. Позначити прапорцем `--no-verify`, щоб Хаскі не скаржився на помилки лінтера
|
||
3. Провести рефакторинг для перенесення у TypeScript в окремому коміті
|
||
|
||
> [!NOTE] Редактори типу VSCode однаково показуватимуть, що файл видалено та створено новий. Якщо ви використаєте CLI для `git add .`, то VSCode показуватиме файл як перейменований
|
||
|
||
### Конвенції про іменування
|
||
|
||
#### Інтерфейси та типи
|
||
|
||
У більшості випадків рекомендовано використовувати оголошення інтерфейсу, а не оголошення типу.
|
||
|
||
Пропси компоненту React — суфікс `Props`
|
||
|
||
```typescript
|
||
interface MyComponentProps {}
|
||
// type MyComponentProps = {};
|
||
const MyComponent = (props: MyComponentProps) => {};
|
||
```
|
||
|
||
Stateful-компоненти React — суфікс `State`
|
||
|
||
```typescript
|
||
interface MyComponentState {}
|
||
// type MyComponentState = {};
|
||
class MyComponent extends Component<MyComponentProps, MyComponentState> {}
|
||
```
|
||
|
||
За замовчуванням — ім’я об’єкта ВерблюдячимРегістром
|
||
|
||
```typescript
|
||
interface MyObject {}
|
||
// type MyObject = {};
|
||
const myObject: MyObject = {};
|
||
```
|
||
|
||
<!-- #### Redux Actions -->
|
||
|
||
<!-- TODO: Once refactored to TS, showcase naming convention for Reducers/Actions and how to type dispatch funcs -->
|
||
|
||
## Redux
|
||
|
||
### Визначення дій
|
||
|
||
```typescript
|
||
enum AppActionTypes = {
|
||
actionFunction = 'actionFunction'
|
||
}
|
||
|
||
export const actionFunction = (
|
||
arg: Arg
|
||
): ReducerPayload<AppActionTypes.actionFunction> => ({
|
||
type: AppActionTypes.actionFunction,
|
||
payload: arg
|
||
});
|
||
```
|
||
|
||
### Як використовувати reduce
|
||
|
||
```typescript
|
||
// Base reducer action without payload
|
||
type ReducerBase<T> = { type: T };
|
||
// Logic for handling optional payloads
|
||
type ReducerPayload<T extends AppActionTypes> =
|
||
T extends AppActionTypes.actionFunction
|
||
? ReducerBase<T> & {
|
||
payload: AppState['property'];
|
||
}
|
||
: ReducerBase<T>;
|
||
|
||
// Switch reducer exported to Redux combineReducers
|
||
export const reducer = (
|
||
state: AppState = initialState,
|
||
action: ReducerPayload<AppActionTypes>
|
||
): AppState => {
|
||
switch (action.type) {
|
||
case AppActionTypes.actionFunction:
|
||
return { ...state, property: action.payload };
|
||
default:
|
||
return state;
|
||
}
|
||
};
|
||
```
|
||
|
||
### Як використовувати dispatch
|
||
|
||
Імпортуйте необхідні дії та селектори всередині компонента.
|
||
|
||
```tsx
|
||
// Add type definition
|
||
interface MyComponentProps {
|
||
actionFunction: typeof actionFunction;
|
||
}
|
||
// Connect to Redux store
|
||
const mapDispatchToProps = {
|
||
actionFunction
|
||
};
|
||
// Example React Component connected to store
|
||
const MyComponent = ({ actionFunction }: MyComponentProps): JSX.Element => {
|
||
const handleClick = () => {
|
||
// Dispatch function
|
||
actionFunction();
|
||
};
|
||
return <button onClick={handleClick}>freeCodeCamp is awesome!</button>;
|
||
};
|
||
|
||
export default connect(null, mapDispatchToProps)(MyComponent);
|
||
```
|
||
|
||
<!-- ### Redux Types File -->
|
||
<!-- The types associated with the Redux store state are located in `client/src/redux/types.ts`... -->
|
||
|
||
## API
|
||
|
||
### Тестування
|
||
|
||
Тести `api/` розділені на дві частини:
|
||
|
||
1. Модульні тести
|
||
2. Інтеграційні тести
|
||
|
||
#### Модульні тести
|
||
|
||
Модульні тести виокремлюють певну функцію чи компонент. Тестам не потрібне мокування, але вони вимагають фікстури.
|
||
|
||
Модульні тести розміщені у новому файлі поруч з експортованим файлом, який тестується:
|
||
|
||
```text
|
||
api/
|
||
├── src/
|
||
│ ├── utils.ts
|
||
│ ├── utils.test.ts
|
||
```
|
||
|
||
#### Інтеграційні тести
|
||
|
||
Інтеграційні тести тестують API загалом. Тести вимагатимуть мокування та не повинні вимагати фікстур, окрім даних заповнення бази даних і методу автентифікації.
|
||
|
||
Зазвичай кожен файл інтеграційного тесту пов’язаний з маршрутом. Інтеграційні тести розміщені у каталозі `api/tests/`:
|
||
|
||
```text
|
||
api/
|
||
├── tests/
|
||
│ ├── settings.ts
|
||
```
|
||
|
||
## Додаткова література
|
||
|
||
- [Документація TypeScript](https://www.typescriptlang.org/docs/)
|
||
- [Шпаргалка для TypeScript із React](https://github.com/typescript-cheatsheets/react#readme)
|