Skip to content
Open
Show file tree
Hide file tree
Changes from 11 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
5 changes: 5 additions & 0 deletions .changeset/brown-rocks-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@soramitsu-ui/ui': patch
---

**refactor**(`SButton`): refactor BEM usage
5 changes: 5 additions & 0 deletions .changeset/dirty-peaches-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@soramitsu-ui/ui': patch
---

**refactor**: move `type-fest` to prod dependencies and update it to `3.1.0`
5 changes: 5 additions & 0 deletions .changeset/shaggy-donkeys-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@soramitsu-ui/ui': patch
---

**chore**: add `@soramitsu-ui/bem` to deps
1 change: 1 addition & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ def pipeline = new org.js.LibPipeline(
buildDockerImage: 'build-tools/node:14-ubuntu-cypress',
npmLoginEmail: '[email protected]',
dockerImageName: 'soramitsu/soramitsu-js-ui-library',
preBuildCmds: ['npm install -g n','n 16.17.0', 'n prune', "yarn install"],
testCmds: ['yarn test:all'],
pushCmds: ['yarn publish-workspaces --no-verify-access'],
libPushBranches: ['master', 'next'],
Expand Down
24 changes: 13 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
"scripts": {
"sb:serve": "yarn --cwd packages/ui sb:serve",
"sb:build": "yarn --cwd packages/ui sb:build",
"test:all": "run-s lint:check test:theme:unit build:vite-plugin-svg test:ui:unit build:theme test:ui:cy build:ui:only-vite test:ui:after-build",
"test:all": "run-s lint:check test:bem:unit test:theme:unit build:bem build:vite-plugin-svg test:ui:unit build:theme test:ui:cy build:ui:only-vite test:ui:after-build",
"test:theme:unit": "yarn --cwd packages/theme test",
"test:bem:unit": "yarn --cwd packages/bem test",
"test:ui:unit": "yarn --cwd packages/ui test:unit",
"test:ui:cy": "yarn --cwd packages/ui cy:ci:component",
"test:ui:after-build": "yarn --cwd packages/ui test:after-build",
"build": "run-s build:theme build:vite-plugin-svg build:ui",
"build": "run-s build:bem build:theme build:vite-plugin-svg build:ui",
"build:theme": "yarn --cwd packages/theme build",
"build:bem": "yarn --cwd packages/bem build",
"build:vite-plugin-svg": "yarn --cwd packages/vite-plugin-svg build",
"build:ui": "yarn --cwd packages/ui build",
"build:ui:only-vite": "yarn --cwd packages/ui build:vite",
Expand All @@ -28,19 +30,19 @@
"devDependencies": {
"@changesets/cli": "^2.17.0",
"@types/node": "^17.0.14",
"@typescript-eslint/eslint-plugin": "^5.12.1",
"@typescript-eslint/parser": "^5.12.1",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"esbuild-jest": "^0.5.0",
"eslint": "^8.16.0",
"eslint-config-alloy": "^4.5.1",
"eslint": "^8.25.0",
"eslint-config-alloy": "^4.7.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-vue": "^9.0.1",
"eslint-plugin-vuejs-accessibility": "^1.1.1",
"eslint-plugin-vue": "^9.6.0",
"eslint-plugin-vuejs-accessibility": "^1.2.0",
"lerna": "^4.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.6.2",
"prettier-eslint": "^15.0.0",
"prettier": "^2.7.1",
"prettier-eslint": "^15.0.1",
"prettier-eslint-cli": "^6.0.1",
"typescript": "4.6.4"
"typescript": "4.8.4"
}
}
102 changes: 102 additions & 0 deletions packages/bem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# @soramitsu-ui/bem

Type-level BEM notation.

## Features

- Statically typed BEM schema
- Less boilerplate - no need to repeat root block name
- Less possibility to make a typo
- Support of classic BEM style (`block__elem_mod-name_mod-key`) and two-dashes (`block__elem--mod-name--mod-key`)

## Example

### Classic style

```ts
const bem = defineBem('v-btn')
// Block modifiers
.mod('loading')
.mod('show-icon')
.mod('icon-size', ['small', 'very-small'] as const)
.mod('icon-size', 'little')
// Block elements
.elem('spinner')
.elem('left-icon', (el) =>
el
// Element modifiers
.mod('active')
.mod('has-stroke')
.mod('right-span', ['big', 'very-big'] as const)
.mod('right-span', 'huge'),
)
.build()

type test = Expect<
Equal<
typeof bem,
{
block: 'v-btn'
block_loading: 'v-btn_loading'
block_showIcon: 'v-btn_show-icon'
block_iconSize_small: 'v-btn_icon-size_small'
block_iconSize_verySmall: 'v-btn_icon-size_very-small'
block_iconSize_little: 'v-btn_icon-size_little'
block__spinner: 'v-btn__spinner'
block__leftIcon: 'v-btn__left-icon'
block__leftIcon_active: 'v-btn__left-icon_active'
block__leftIcon_hasStroke: 'v-btn__left-icon_has-stroke'
block__leftIcon_rightSpan_big: 'v-btn__left-icon_right-span_big'
block__leftIcon_rightSpan_veryBig: 'v-btn__left-icon_right-span_very-big'
block__leftIcon_rightSpan_huge: 'v-btn__left-icon_right-span_huge'
}
>
>
```

### Two-dashes style

```ts
const bem = defineBem('v-btn')
.mod('loading')
.mod('show-icon')
.mod('icon-size', ['small', 'very-small'] as const)
.mod('icon-size', 'little')
.elem('spinner')
.elem('left-icon', (el) =>
el
//
.mod('active')
.mod('has-stroke')
.mod('right-span', ['big', 'very-big'] as const)
.mod('right-span', 'huge'),
)
.build('two-dashes')

type test = Expect<
Equal<
typeof bem,
{
block: 'v-btn'
block_loading: 'v-btn--loading'
block_showIcon: 'v-btn--show-icon'
block_iconSize_small: 'v-btn--icon-size--small'
block_iconSize_verySmall: 'v-btn--icon-size--very-small'
block_iconSize_little: 'v-btn--icon-size--little'
block__spinner: 'v-btn__spinner'
block__leftIcon: 'v-btn__left-icon'
block__leftIcon_active: 'v-btn__left-icon--active'
block__leftIcon_hasStroke: 'v-btn__left-icon--has-stroke'
block__leftIcon_rightSpan_big: 'v-btn__left-icon--right-span--big'
block__leftIcon_rightSpan_veryBig: 'v-btn__left-icon--right-span--very-big'
block__leftIcon_rightSpan_huge: 'v-btn__left-icon--right-span--huge'
}
>
>
```

## Install

```bash
npm i @soramitsu-ui/bem
```
34 changes: 34 additions & 0 deletions packages/bem/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "@soramitsu-ui/bem",
"version": "1.0.0",
"description": "Type-level BEM notation",
"type": "module",
"exports": {
".": {
"import": "./dist/lib.mjs",
"require": "./dist/lib.cjs",
"types": "./dist/lib.d.ts"
}
},
"main": "./dist/lib.cjs",
"types": "./dist/lib.d.ts",
"files": [
"dist"
],
"license": "Apache-2.0",
"publishConfig": {
"access": "public"
},
"dependencies": {
"fast-case": "^1.7.0",
"type-fest": "^3.1.0"
},
"devDependencies": {
"unbuild": "^0.9.4",
"vitest": "^0.24.3"
},
"scripts": {
"build": "unbuild",
"test": "vitest run"
}
}
137 changes: 137 additions & 0 deletions packages/bem/src/lib.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { test, expect, describe } from 'vitest'
import { defineBem } from './lib'
import { Expect, Equal } from './types'

describe('defineBem', () => {
const complexBem = defineBem('v-btn')
// Block modifiers
.mod('loading')
.mod('show-icon')
.mod('icon-size', ['small', 'very-small'] as const)
.mod('icon-size', 'little')
// Block elements
.elem('spinner')
.elem('left-icon', (el) =>
el
// Element modifiers
.mod('active')
.mod('has-stroke')
.mod('right-span', ['big', 'very-big'] as const)
.mod('right-span', 'huge'),
)

test('Only block', () => {
const bem = defineBem('s-table').build()

type test = Expect<Equal<typeof bem, { block: 's-table' }>>

expect(test).toMatchInlineSnapshot('[Function]')
})

test('Simple block', () => {
const bem = defineBem('block').elem('elem').build()

type test = Expect<
Equal<
typeof bem,
{
block: 'block'
block__elem: 'block__elem'
}
>
>

expect(bem).toMatchInlineSnapshot(`
{
"block": "block",
"block__elem": "block__elem",
}
`)
})

test('Complex classic build', () => {
const bem = complexBem.build()

type test = Expect<
Equal<
typeof bem,
{
block: 'v-btn'
block_loading: 'v-btn_loading'
block_showIcon: 'v-btn_show-icon'
block_iconSize_small: 'v-btn_icon-size_small'
block_iconSize_verySmall: 'v-btn_icon-size_very-small'
block_iconSize_little: 'v-btn_icon-size_little'
block__spinner: 'v-btn__spinner'
block__leftIcon: 'v-btn__left-icon'
block__leftIcon_active: 'v-btn__left-icon_active'
block__leftIcon_hasStroke: 'v-btn__left-icon_has-stroke'
block__leftIcon_rightSpan_big: 'v-btn__left-icon_right-span_big'
block__leftIcon_rightSpan_veryBig: 'v-btn__left-icon_right-span_very-big'
block__leftIcon_rightSpan_huge: 'v-btn__left-icon_right-span_huge'
}
>
>

expect(bem).toMatchInlineSnapshot(`
{
"block": "v-btn",
"block__leftIcon": "v-btn__left-icon",
"block__leftIcon_active": "v-btn__left-icon_active",
"block__leftIcon_hasStroke": "v-btn__left-icon_has-stroke",
"block__leftIcon_rightSpan_big": "v-btn__left-icon_right-span_big",
"block__leftIcon_rightSpan_huge": "v-btn__left-icon_right-span_huge",
"block__leftIcon_rightSpan_veryBig": "v-btn__left-icon_right-span_very-big",
"block__spinner": "v-btn__spinner",
"block_iconSize_little": "v-btn_icon-size_little",
"block_iconSize_small": "v-btn_icon-size_small",
"block_iconSize_verySmall": "v-btn_icon-size_very-small",
"block_loading": "v-btn_loading",
"block_showIcon": "v-btn_show-icon",
}
`)
})

test('Complex two-dashes build', () => {
const bem = complexBem.build('two-dashes')

type test = Expect<
Equal<
typeof bem,
{
block: 'v-btn'
block_loading: 'v-btn--loading'
block_showIcon: 'v-btn--show-icon'
block_iconSize_small: 'v-btn--icon-size--small'
block_iconSize_verySmall: 'v-btn--icon-size--very-small'
block_iconSize_little: 'v-btn--icon-size--little'
block__spinner: 'v-btn__spinner'
block__leftIcon: 'v-btn__left-icon'
block__leftIcon_active: 'v-btn__left-icon--active'
block__leftIcon_hasStroke: 'v-btn__left-icon--has-stroke'
block__leftIcon_rightSpan_big: 'v-btn__left-icon--right-span--big'
block__leftIcon_rightSpan_veryBig: 'v-btn__left-icon--right-span--very-big'
block__leftIcon_rightSpan_huge: 'v-btn__left-icon--right-span--huge'
}
>
>

expect(bem).toMatchInlineSnapshot(`
{
"block": "v-btn",
"block__leftIcon": "v-btn__left-icon",
"block__leftIcon_active": "v-btn__left-icon--active",
"block__leftIcon_hasStroke": "v-btn__left-icon--has-stroke",
"block__leftIcon_rightSpan_big": "v-btn__left-icon--right-span--big",
"block__leftIcon_rightSpan_huge": "v-btn__left-icon--right-span--huge",
"block__leftIcon_rightSpan_veryBig": "v-btn__left-icon--right-span--very-big",
"block__spinner": "v-btn__spinner",
"block_iconSize_little": "v-btn--icon-size--little",
"block_iconSize_small": "v-btn--icon-size--small",
"block_iconSize_verySmall": "v-btn--icon-size--very-small",
"block_loading": "v-btn--loading",
"block_showIcon": "v-btn--show-icon",
}
`)
})
})
8 changes: 8 additions & 0 deletions packages/bem/src/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { BlockBuilder } from './types'
import { BemBlock } from './unchecked-builder'

export const defineBem: <r extends string>(blockName: r) => BlockBuilder<r> = (blockName) => {
return new BemBlock(blockName) as unknown as BlockBuilder<any>
}

export type { BlockBuilder, BemStyle } from './types'
Loading