├── .all-contributorsrc ├── .babelrc.js ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── flow-typed └── npm │ ├── enzyme_v3.x.x.js │ └── jest_v22.x.x.js ├── index.d.ts ├── jestSetup.js ├── license ├── package.json ├── readme.md ├── rollup.config.js ├── src ├── elements.js ├── index.js └── index.test.js └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "styled-style", 3 | "projectOwner": "akameco", 4 | "files": [ 5 | "readme.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": false, 9 | "contributors": [ 10 | { 11 | "login": "akameco", 12 | "name": "akameco", 13 | "avatar_url": "https://avatars2.githubusercontent.com/u/4002137?v=4", 14 | "profile": "http://akameco.github.io", 15 | "contributions": [ 16 | "code", 17 | "doc", 18 | "test", 19 | "infra" 20 | ] 21 | }, 22 | { 23 | "login": "nju33", 24 | "name": "純", 25 | "avatar_url": "https://avatars2.githubusercontent.com/u/15901038?v=4", 26 | "profile": "https://nju33.work", 27 | "contributions": [ 28 | "code" 29 | ] 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /.babelrc.js: -------------------------------------------------------------------------------- 1 | const { NODE_ENV } = process.env 2 | 3 | const modules = NODE_ENV === 'test' ? 'commonjs' : false 4 | 5 | module.exports = { 6 | presets: [ 7 | [ 8 | '@babel/preset-env', 9 | { 10 | targets: { 11 | browsers: ['last 2 versions', 'safari >= 7'], 12 | }, 13 | loose: true, 14 | modules, 15 | }, 16 | ], 17 | '@babel/preset-react', 18 | '@babel/preset-flow', 19 | ], 20 | plugins: [ 21 | '@babel/plugin-proposal-object-rest-spread', 22 | ['@babel/plugin-proposal-class-properties', { loose: true }], 23 | ], 24 | } 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/coverage/** 2 | **/flow-typed/** 3 | **/lib/** 4 | rollup.config.js 5 | index.d.ts 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "precure/auto" 3 | } 4 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | coverage 3 | lib 4 | 5 | [include] 6 | 7 | [libs] 8 | 9 | [lints] 10 | 11 | [options] 12 | 13 | [strict] 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | - version: 10 | - `node` version: 11 | - `npm` (or `yarn`) version: 12 | 13 | **Do you want to request a *feature* or report a *bug*?:** 14 | 15 | **What is the current behavior?:** 16 | 17 | **What is the expected behavior?:** 18 | 19 | **Suggested solution:** 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | **What**: 8 | 9 | 10 | 11 | **Why**: 12 | 13 | 14 | 15 | **How**: 16 | 17 | 18 | **Checklist**: 19 | 20 | 21 | * [ ] Documentation 22 | * [ ] Tests 23 | * [ ] Ready to be merged 24 | * [ ] Added myself to contributors table 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | lib 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | flow-typed 2 | lib 3 | coverage 4 | .github 5 | package.json 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | semi: false 2 | singleQuote: true 3 | trailingComma: es5 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '10' 4 | 5 | cache: 6 | yarn: true 7 | directories: 8 | - ".eslintcache" 9 | - "node_modules" 10 | 11 | script: 12 | - yarn run test:ci 13 | 14 | notifications: 15 | email: false 16 | 17 | branches: 18 | only: master 19 | -------------------------------------------------------------------------------- /flow-typed/npm/enzyme_v3.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 02db3523747059d89e87d4dec6873edf 2 | // flow-typed version: 62a0c60689/enzyme_v3.x.x/flow_>=v0.53.x 3 | 4 | import * as React from "react"; 5 | 6 | declare module "enzyme" { 7 | declare type PredicateFunction = ( 8 | wrapper: T, 9 | index: number 10 | ) => boolean; 11 | declare type NodeOrNodes = React.Node | Array; 12 | declare type EnzymeSelector = string | Class> | Object; 13 | 14 | // CheerioWrapper is a type alias for an actual cheerio instance 15 | // TODO: Reference correct type from cheerio's type declarations 16 | declare type CheerioWrapper = any; 17 | 18 | declare class Wrapper { 19 | find(selector: EnzymeSelector): this, 20 | findWhere(predicate: PredicateFunction): this, 21 | filter(selector: EnzymeSelector): this, 22 | filterWhere(predicate: PredicateFunction): this, 23 | hostNodes(): this, 24 | contains(nodeOrNodes: NodeOrNodes): boolean, 25 | containsMatchingElement(node: React.Node): boolean, 26 | containsAllMatchingElements(nodes: NodeOrNodes): boolean, 27 | containsAnyMatchingElements(nodes: NodeOrNodes): boolean, 28 | dive(option?: { context?: Object }): this, 29 | exists(): boolean, 30 | isEmptyRender(): boolean, 31 | matchesElement(node: React.Node): boolean, 32 | hasClass(className: string): boolean, 33 | is(selector: EnzymeSelector): boolean, 34 | isEmpty(): boolean, 35 | not(selector: EnzymeSelector): this, 36 | children(selector?: EnzymeSelector): this, 37 | childAt(index: number): this, 38 | parents(selector?: EnzymeSelector): this, 39 | parent(): this, 40 | closest(selector: EnzymeSelector): this, 41 | render(): CheerioWrapper, 42 | unmount(): this, 43 | text(): string, 44 | html(): string, 45 | get(index: number): React.Node, 46 | getNodes(): Array, 47 | getDOMNode(): HTMLElement | HTMLInputElement, 48 | at(index: number): this, 49 | first(): this, 50 | last(): this, 51 | state(key?: string): any, 52 | context(key?: string): any, 53 | props(): Object, 54 | prop(key: string): any, 55 | key(): string, 56 | simulate(event: string, ...args: Array): this, 57 | setState(state: {}, callback?: Function): this, 58 | setProps(props: {}): this, 59 | setContext(context: Object): this, 60 | instance(): React.Component<*, *>, 61 | update(): this, 62 | debug(): string, 63 | type(): string | Function | null, 64 | name(): string, 65 | forEach(fn: (node: this, index: number) => mixed): this, 66 | map(fn: (node: this, index: number) => T): Array, 67 | reduce( 68 | fn: (value: T, node: this, index: number) => T, 69 | initialValue?: T 70 | ): Array, 71 | reduceRight( 72 | fn: (value: T, node: this, index: number) => T, 73 | initialValue?: T 74 | ): Array, 75 | some(selector: EnzymeSelector): boolean, 76 | someWhere(predicate: PredicateFunction): boolean, 77 | every(selector: EnzymeSelector): boolean, 78 | everyWhere(predicate: PredicateFunction): boolean, 79 | length: number 80 | } 81 | 82 | declare class ReactWrapper extends Wrapper { 83 | constructor(nodes: NodeOrNodes, root: any, options?: ?Object): ReactWrapper, 84 | mount(): this, 85 | ref(refName: string): this, 86 | detach(): void 87 | } 88 | 89 | declare class ShallowWrapper extends Wrapper { 90 | constructor( 91 | nodes: NodeOrNodes, 92 | root: any, 93 | options?: ?Object 94 | ): ShallowWrapper, 95 | equals(node: React.Node): boolean, 96 | shallow(options?: { context?: Object }): ShallowWrapper 97 | } 98 | 99 | declare function shallow( 100 | node: React.Node, 101 | options?: { context?: Object, disableLifecycleMethods?: boolean } 102 | ): ShallowWrapper; 103 | declare function mount( 104 | node: React.Node, 105 | options?: { 106 | context?: Object, 107 | attachTo?: HTMLElement, 108 | childContextTypes?: Object 109 | } 110 | ): ReactWrapper; 111 | declare function render( 112 | node: React.Node, 113 | options?: { context?: Object } 114 | ): CheerioWrapper; 115 | 116 | declare module.exports: { 117 | configure(options: { 118 | Adapter?: any, 119 | disableLifecycleMethods?: boolean 120 | }): void, 121 | render: typeof render, 122 | mount: typeof mount, 123 | shallow: typeof shallow, 124 | ShallowWrapper: typeof ShallowWrapper, 125 | ReactWrapper: typeof ReactWrapper 126 | }; 127 | } 128 | -------------------------------------------------------------------------------- /flow-typed/npm/jest_v22.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: 6e1fc0a644aa956f79029fec0709e597 2 | // flow-typed version: 07ebad4796/jest_v22.x.x/flow_>=v0.39.x 3 | 4 | type JestMockFn, TReturn> = { 5 | (...args: TArguments): TReturn, 6 | /** 7 | * An object for introspecting mock calls 8 | */ 9 | mock: { 10 | /** 11 | * An array that represents all calls that have been made into this mock 12 | * function. Each call is represented by an array of arguments that were 13 | * passed during the call. 14 | */ 15 | calls: Array, 16 | /** 17 | * An array that contains all the object instances that have been 18 | * instantiated from this mock function. 19 | */ 20 | instances: Array 21 | }, 22 | /** 23 | * Resets all information stored in the mockFn.mock.calls and 24 | * mockFn.mock.instances arrays. Often this is useful when you want to clean 25 | * up a mock's usage data between two assertions. 26 | */ 27 | mockClear(): void, 28 | /** 29 | * Resets all information stored in the mock. This is useful when you want to 30 | * completely restore a mock back to its initial state. 31 | */ 32 | mockReset(): void, 33 | /** 34 | * Removes the mock and restores the initial implementation. This is useful 35 | * when you want to mock functions in certain test cases and restore the 36 | * original implementation in others. Beware that mockFn.mockRestore only 37 | * works when mock was created with jest.spyOn. Thus you have to take care of 38 | * restoration yourself when manually assigning jest.fn(). 39 | */ 40 | mockRestore(): void, 41 | /** 42 | * Accepts a function that should be used as the implementation of the mock. 43 | * The mock itself will still record all calls that go into and instances 44 | * that come from itself -- the only difference is that the implementation 45 | * will also be executed when the mock is called. 46 | */ 47 | mockImplementation( 48 | fn: (...args: TArguments) => TReturn 49 | ): JestMockFn, 50 | /** 51 | * Accepts a function that will be used as an implementation of the mock for 52 | * one call to the mocked function. Can be chained so that multiple function 53 | * calls produce different results. 54 | */ 55 | mockImplementationOnce( 56 | fn: (...args: TArguments) => TReturn 57 | ): JestMockFn, 58 | /** 59 | * Just a simple sugar function for returning `this` 60 | */ 61 | mockReturnThis(): void, 62 | /** 63 | * Deprecated: use jest.fn(() => value) instead 64 | */ 65 | mockReturnValue(value: TReturn): JestMockFn, 66 | /** 67 | * Sugar for only returning a value once inside your mock 68 | */ 69 | mockReturnValueOnce(value: TReturn): JestMockFn 70 | }; 71 | 72 | type JestAsymmetricEqualityType = { 73 | /** 74 | * A custom Jasmine equality tester 75 | */ 76 | asymmetricMatch(value: mixed): boolean 77 | }; 78 | 79 | type JestCallsType = { 80 | allArgs(): mixed, 81 | all(): mixed, 82 | any(): boolean, 83 | count(): number, 84 | first(): mixed, 85 | mostRecent(): mixed, 86 | reset(): void 87 | }; 88 | 89 | type JestClockType = { 90 | install(): void, 91 | mockDate(date: Date): void, 92 | tick(milliseconds?: number): void, 93 | uninstall(): void 94 | }; 95 | 96 | type JestMatcherResult = { 97 | message?: string | (() => string), 98 | pass: boolean 99 | }; 100 | 101 | type JestMatcher = (actual: any, expected: any) => JestMatcherResult; 102 | 103 | type JestPromiseType = { 104 | /** 105 | * Use rejects to unwrap the reason of a rejected promise so any other 106 | * matcher can be chained. If the promise is fulfilled the assertion fails. 107 | */ 108 | rejects: JestExpectType, 109 | /** 110 | * Use resolves to unwrap the value of a fulfilled promise so any other 111 | * matcher can be chained. If the promise is rejected the assertion fails. 112 | */ 113 | resolves: JestExpectType 114 | }; 115 | 116 | /** 117 | * Plugin: jest-enzyme 118 | */ 119 | type EnzymeMatchersType = { 120 | toBeChecked(): void, 121 | toBeDisabled(): void, 122 | toBeEmpty(): void, 123 | toBePresent(): void, 124 | toContainReact(element: React$Element): void, 125 | toHaveClassName(className: string): void, 126 | toHaveHTML(html: string): void, 127 | toHaveProp(propKey: string, propValue?: any): void, 128 | toHaveRef(refName: string): void, 129 | toHaveState(stateKey: string, stateValue?: any): void, 130 | toHaveStyle(styleKey: string, styleValue?: any): void, 131 | toHaveTagName(tagName: string): void, 132 | toHaveText(text: string): void, 133 | toIncludeText(text: string): void, 134 | toHaveValue(value: any): void, 135 | toMatchElement(element: React$Element): void, 136 | toMatchSelector(selector: string): void 137 | }; 138 | 139 | type JestExpectType = { 140 | not: JestExpectType & EnzymeMatchersType, 141 | /** 142 | * If you have a mock function, you can use .lastCalledWith to test what 143 | * arguments it was last called with. 144 | */ 145 | lastCalledWith(...args: Array): void, 146 | /** 147 | * toBe just checks that a value is what you expect. It uses === to check 148 | * strict equality. 149 | */ 150 | toBe(value: any): void, 151 | /** 152 | * Use .toHaveBeenCalled to ensure that a mock function got called. 153 | */ 154 | toBeCalled(): void, 155 | /** 156 | * Use .toBeCalledWith to ensure that a mock function was called with 157 | * specific arguments. 158 | */ 159 | toBeCalledWith(...args: Array): void, 160 | /** 161 | * Using exact equality with floating point numbers is a bad idea. Rounding 162 | * means that intuitive things fail. 163 | */ 164 | toBeCloseTo(num: number, delta: any): void, 165 | /** 166 | * Use .toBeDefined to check that a variable is not undefined. 167 | */ 168 | toBeDefined(): void, 169 | /** 170 | * Use .toBeFalsy when you don't care what a value is, you just want to 171 | * ensure a value is false in a boolean context. 172 | */ 173 | toBeFalsy(): void, 174 | /** 175 | * To compare floating point numbers, you can use toBeGreaterThan. 176 | */ 177 | toBeGreaterThan(number: number): void, 178 | /** 179 | * To compare floating point numbers, you can use toBeGreaterThanOrEqual. 180 | */ 181 | toBeGreaterThanOrEqual(number: number): void, 182 | /** 183 | * To compare floating point numbers, you can use toBeLessThan. 184 | */ 185 | toBeLessThan(number: number): void, 186 | /** 187 | * To compare floating point numbers, you can use toBeLessThanOrEqual. 188 | */ 189 | toBeLessThanOrEqual(number: number): void, 190 | /** 191 | * Use .toBeInstanceOf(Class) to check that an object is an instance of a 192 | * class. 193 | */ 194 | toBeInstanceOf(cls: Class<*>): void, 195 | /** 196 | * .toBeNull() is the same as .toBe(null) but the error messages are a bit 197 | * nicer. 198 | */ 199 | toBeNull(): void, 200 | /** 201 | * Use .toBeTruthy when you don't care what a value is, you just want to 202 | * ensure a value is true in a boolean context. 203 | */ 204 | toBeTruthy(): void, 205 | /** 206 | * Use .toBeUndefined to check that a variable is undefined. 207 | */ 208 | toBeUndefined(): void, 209 | /** 210 | * Use .toContain when you want to check that an item is in a list. For 211 | * testing the items in the list, this uses ===, a strict equality check. 212 | */ 213 | toContain(item: any): void, 214 | /** 215 | * Use .toContainEqual when you want to check that an item is in a list. For 216 | * testing the items in the list, this matcher recursively checks the 217 | * equality of all fields, rather than checking for object identity. 218 | */ 219 | toContainEqual(item: any): void, 220 | /** 221 | * Use .toEqual when you want to check that two objects have the same value. 222 | * This matcher recursively checks the equality of all fields, rather than 223 | * checking for object identity. 224 | */ 225 | toEqual(value: any): void, 226 | /** 227 | * Use .toHaveBeenCalled to ensure that a mock function got called. 228 | */ 229 | toHaveBeenCalled(): void, 230 | /** 231 | * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact 232 | * number of times. 233 | */ 234 | toHaveBeenCalledTimes(number: number): void, 235 | /** 236 | * Use .toHaveBeenCalledWith to ensure that a mock function was called with 237 | * specific arguments. 238 | */ 239 | toHaveBeenCalledWith(...args: Array): void, 240 | /** 241 | * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called 242 | * with specific arguments. 243 | */ 244 | toHaveBeenLastCalledWith(...args: Array): void, 245 | /** 246 | * Check that an object has a .length property and it is set to a certain 247 | * numeric value. 248 | */ 249 | toHaveLength(number: number): void, 250 | /** 251 | * 252 | */ 253 | toHaveProperty(propPath: string, value?: any): void, 254 | /** 255 | * Use .toMatch to check that a string matches a regular expression or string. 256 | */ 257 | toMatch(regexpOrString: RegExp | string): void, 258 | /** 259 | * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. 260 | */ 261 | toMatchObject(object: Object | Array): void, 262 | /** 263 | * This ensures that a React component matches the most recent snapshot. 264 | */ 265 | toMatchSnapshot(name?: string): void, 266 | /** 267 | * Use .toThrow to test that a function throws when it is called. 268 | * If you want to test that a specific error gets thrown, you can provide an 269 | * argument to toThrow. The argument can be a string for the error message, 270 | * a class for the error, or a regex that should match the error. 271 | * 272 | * Alias: .toThrowError 273 | */ 274 | toThrow(message?: string | Error | Class | RegExp): void, 275 | toThrowError(message?: string | Error | Class | RegExp): void, 276 | /** 277 | * Use .toThrowErrorMatchingSnapshot to test that a function throws a error 278 | * matching the most recent snapshot when it is called. 279 | */ 280 | toThrowErrorMatchingSnapshot(): void 281 | }; 282 | 283 | type JestObjectType = { 284 | /** 285 | * Disables automatic mocking in the module loader. 286 | * 287 | * After this method is called, all `require()`s will return the real 288 | * versions of each module (rather than a mocked version). 289 | */ 290 | disableAutomock(): JestObjectType, 291 | /** 292 | * An un-hoisted version of disableAutomock 293 | */ 294 | autoMockOff(): JestObjectType, 295 | /** 296 | * Enables automatic mocking in the module loader. 297 | */ 298 | enableAutomock(): JestObjectType, 299 | /** 300 | * An un-hoisted version of enableAutomock 301 | */ 302 | autoMockOn(): JestObjectType, 303 | /** 304 | * Clears the mock.calls and mock.instances properties of all mocks. 305 | * Equivalent to calling .mockClear() on every mocked function. 306 | */ 307 | clearAllMocks(): JestObjectType, 308 | /** 309 | * Resets the state of all mocks. Equivalent to calling .mockReset() on every 310 | * mocked function. 311 | */ 312 | resetAllMocks(): JestObjectType, 313 | /** 314 | * Restores all mocks back to their original value. 315 | */ 316 | restoreAllMocks(): JestObjectType, 317 | /** 318 | * Removes any pending timers from the timer system. 319 | */ 320 | clearAllTimers(): void, 321 | /** 322 | * The same as `mock` but not moved to the top of the expectation by 323 | * babel-jest. 324 | */ 325 | doMock(moduleName: string, moduleFactory?: any): JestObjectType, 326 | /** 327 | * The same as `unmock` but not moved to the top of the expectation by 328 | * babel-jest. 329 | */ 330 | dontMock(moduleName: string): JestObjectType, 331 | /** 332 | * Returns a new, unused mock function. Optionally takes a mock 333 | * implementation. 334 | */ 335 | fn, TReturn>( 336 | implementation?: (...args: TArguments) => TReturn 337 | ): JestMockFn, 338 | /** 339 | * Determines if the given function is a mocked function. 340 | */ 341 | isMockFunction(fn: Function): boolean, 342 | /** 343 | * Given the name of a module, use the automatic mocking system to generate a 344 | * mocked version of the module for you. 345 | */ 346 | genMockFromModule(moduleName: string): any, 347 | /** 348 | * Mocks a module with an auto-mocked version when it is being required. 349 | * 350 | * The second argument can be used to specify an explicit module factory that 351 | * is being run instead of using Jest's automocking feature. 352 | * 353 | * The third argument can be used to create virtual mocks -- mocks of modules 354 | * that don't exist anywhere in the system. 355 | */ 356 | mock( 357 | moduleName: string, 358 | moduleFactory?: any, 359 | options?: Object 360 | ): JestObjectType, 361 | /** 362 | * Returns the actual module instead of a mock, bypassing all checks on 363 | * whether the module should receive a mock implementation or not. 364 | */ 365 | requireActual(moduleName: string): any, 366 | /** 367 | * Returns a mock module instead of the actual module, bypassing all checks 368 | * on whether the module should be required normally or not. 369 | */ 370 | requireMock(moduleName: string): any, 371 | /** 372 | * Resets the module registry - the cache of all required modules. This is 373 | * useful to isolate modules where local state might conflict between tests. 374 | */ 375 | resetModules(): JestObjectType, 376 | /** 377 | * Exhausts the micro-task queue (usually interfaced in node via 378 | * process.nextTick). 379 | */ 380 | runAllTicks(): void, 381 | /** 382 | * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), 383 | * setInterval(), and setImmediate()). 384 | */ 385 | runAllTimers(): void, 386 | /** 387 | * Exhausts all tasks queued by setImmediate(). 388 | */ 389 | runAllImmediates(): void, 390 | /** 391 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout() 392 | * or setInterval() and setImmediate()). 393 | */ 394 | runTimersToTime(msToRun: number): void, 395 | /** 396 | * Executes only the macro-tasks that are currently pending (i.e., only the 397 | * tasks that have been queued by setTimeout() or setInterval() up to this 398 | * point) 399 | */ 400 | runOnlyPendingTimers(): void, 401 | /** 402 | * Explicitly supplies the mock object that the module system should return 403 | * for the specified module. Note: It is recommended to use jest.mock() 404 | * instead. 405 | */ 406 | setMock(moduleName: string, moduleExports: any): JestObjectType, 407 | /** 408 | * Indicates that the module system should never return a mocked version of 409 | * the specified module from require() (e.g. that it should always return the 410 | * real module). 411 | */ 412 | unmock(moduleName: string): JestObjectType, 413 | /** 414 | * Instructs Jest to use fake versions of the standard timer functions 415 | * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, 416 | * setImmediate and clearImmediate). 417 | */ 418 | useFakeTimers(): JestObjectType, 419 | /** 420 | * Instructs Jest to use the real versions of the standard timer functions. 421 | */ 422 | useRealTimers(): JestObjectType, 423 | /** 424 | * Creates a mock function similar to jest.fn but also tracks calls to 425 | * object[methodName]. 426 | */ 427 | spyOn(object: Object, methodName: string): JestMockFn, 428 | /** 429 | * Set the default timeout interval for tests and before/after hooks in milliseconds. 430 | * Note: The default timeout interval is 5 seconds if this method is not called. 431 | */ 432 | setTimeout(timeout: number): JestObjectType 433 | }; 434 | 435 | type JestSpyType = { 436 | calls: JestCallsType 437 | }; 438 | 439 | /** Runs this function after every test inside this context */ 440 | declare function afterEach( 441 | fn: (done: () => void) => ?Promise, 442 | timeout?: number 443 | ): void; 444 | /** Runs this function before every test inside this context */ 445 | declare function beforeEach( 446 | fn: (done: () => void) => ?Promise, 447 | timeout?: number 448 | ): void; 449 | /** Runs this function after all tests have finished inside this context */ 450 | declare function afterAll( 451 | fn: (done: () => void) => ?Promise, 452 | timeout?: number 453 | ): void; 454 | /** Runs this function before any tests have started inside this context */ 455 | declare function beforeAll( 456 | fn: (done: () => void) => ?Promise, 457 | timeout?: number 458 | ): void; 459 | 460 | /** A context for grouping tests together */ 461 | declare var describe: { 462 | /** 463 | * Creates a block that groups together several related tests in one "test suite" 464 | */ 465 | (name: string, fn: () => void): void, 466 | 467 | /** 468 | * Only run this describe block 469 | */ 470 | only(name: string, fn: () => void): void, 471 | 472 | /** 473 | * Skip running this describe block 474 | */ 475 | skip(name: string, fn: () => void): void 476 | }; 477 | 478 | /** An individual test unit */ 479 | declare var it: { 480 | /** 481 | * An individual test unit 482 | * 483 | * @param {string} Name of Test 484 | * @param {Function} Test 485 | * @param {number} Timeout for the test, in milliseconds. 486 | */ 487 | ( 488 | name: string, 489 | fn?: (done: () => void) => ?Promise, 490 | timeout?: number 491 | ): void, 492 | /** 493 | * Only run this test 494 | * 495 | * @param {string} Name of Test 496 | * @param {Function} Test 497 | * @param {number} Timeout for the test, in milliseconds. 498 | */ 499 | only( 500 | name: string, 501 | fn?: (done: () => void) => ?Promise, 502 | timeout?: number 503 | ): void, 504 | /** 505 | * Skip running this test 506 | * 507 | * @param {string} Name of Test 508 | * @param {Function} Test 509 | * @param {number} Timeout for the test, in milliseconds. 510 | */ 511 | skip( 512 | name: string, 513 | fn?: (done: () => void) => ?Promise, 514 | timeout?: number 515 | ): void, 516 | /** 517 | * Run the test concurrently 518 | * 519 | * @param {string} Name of Test 520 | * @param {Function} Test 521 | * @param {number} Timeout for the test, in milliseconds. 522 | */ 523 | concurrent( 524 | name: string, 525 | fn?: (done: () => void) => ?Promise, 526 | timeout?: number 527 | ): void 528 | }; 529 | declare function fit( 530 | name: string, 531 | fn: (done: () => void) => ?Promise, 532 | timeout?: number 533 | ): void; 534 | /** An individual test unit */ 535 | declare var test: typeof it; 536 | /** A disabled group of tests */ 537 | declare var xdescribe: typeof describe; 538 | /** A focused group of tests */ 539 | declare var fdescribe: typeof describe; 540 | /** A disabled individual test */ 541 | declare var xit: typeof it; 542 | /** A disabled individual test */ 543 | declare var xtest: typeof it; 544 | 545 | /** The expect function is used every time you want to test a value */ 546 | declare var expect: { 547 | /** The object that you want to make assertions against */ 548 | (value: any): JestExpectType & JestPromiseType & EnzymeMatchersType, 549 | /** Add additional Jasmine matchers to Jest's roster */ 550 | extend(matchers: { [name: string]: JestMatcher }): void, 551 | /** Add a module that formats application-specific data structures. */ 552 | addSnapshotSerializer(serializer: (input: Object) => string): void, 553 | assertions(expectedAssertions: number): void, 554 | hasAssertions(): void, 555 | any(value: mixed): JestAsymmetricEqualityType, 556 | anything(): void, 557 | arrayContaining(value: Array): void, 558 | objectContaining(value: Object): void, 559 | /** Matches any received string that contains the exact expected string. */ 560 | stringContaining(value: string): void, 561 | stringMatching(value: string | RegExp): void 562 | }; 563 | 564 | // TODO handle return type 565 | // http://jasmine.github.io/2.4/introduction.html#section-Spies 566 | declare function spyOn(value: mixed, method: string): Object; 567 | 568 | /** Holds all functions related to manipulating test runner */ 569 | declare var jest: JestObjectType; 570 | 571 | /** 572 | * The global Jasmine object, this is generally not exposed as the public API, 573 | * using features inside here could break in later versions of Jest. 574 | */ 575 | declare var jasmine: { 576 | DEFAULT_TIMEOUT_INTERVAL: number, 577 | any(value: mixed): JestAsymmetricEqualityType, 578 | anything(): void, 579 | arrayContaining(value: Array): void, 580 | clock(): JestClockType, 581 | createSpy(name: string): JestSpyType, 582 | createSpyObj( 583 | baseName: string, 584 | methodNames: Array 585 | ): { [methodName: string]: JestSpyType }, 586 | objectContaining(value: Object): void, 587 | stringMatching(value: string): void 588 | }; 589 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | 3 | declare function styledStyle( 4 | styles: S 5 | ): Record< 6 | styledStyle.Elements, 7 |

( 8 | selector: styledStyle.Selector 9 | ) => React.ComponentClass<{ children?: React.ReactNode } & P> 10 | > 11 | 12 | export { styledStyle } 13 | 14 | declare namespace styledStyle { 15 | export type Elements = keyof React.ReactDOM 16 | 17 | export interface Styles { 18 | [key: string]: string 19 | } 20 | 21 | export type _Selector = 22 | | C 23 | | ((props: P) => C | string | null | undefined) 24 | export type Selector = C | _Selector[] 25 | } 26 | -------------------------------------------------------------------------------- /jestSetup.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-extraneous-dependencies */ 2 | // @flow 3 | import Enzyme from 'enzyme' 4 | import Adapter from 'enzyme-adapter-react-16' 5 | 6 | // Configure Enzyme for React 7 | Enzyme.configure({ adapter: new Adapter() }) 8 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) akameco (akameco.github.io) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "styled-style", 3 | "version": "0.6.2", 4 | "description": "css modules like styled-components", 5 | "license": "MIT", 6 | "repository": "akameco/styled-style", 7 | "author": "akameco (akameco.github.io)", 8 | "engines": { 9 | "node": ">=6" 10 | }, 11 | "main": "lib/index.js", 12 | "jsnext:main": "lib/index.esm.js", 13 | "module": "lib/index.esm.js", 14 | "types": "index.d.ts", 15 | "scripts": { 16 | "add-contributor": "all-contributors add", 17 | "fmt": "prettier --write '**/*.{js,json,md}'", 18 | "flow": "flow", 19 | "lint": "eslint --fix .", 20 | "test": "jest", 21 | "prepare": "yarn build && yarn build:flow", 22 | "build": "yarn rollup && yarn build:flow", 23 | "rollup": "rollup -c", 24 | "build:lib": "babel src -d lib --ignore '**/*.test.js'", 25 | "build:flow": "flow-copy-source -v -i '*.test.js' src lib", 26 | "test:watch": "jest --watch", 27 | "test:cov": "jest --coverage --ci --runInBand", 28 | "test:ci": "yarn flow && yarn lint && yarn test:cov" 29 | }, 30 | "lint-staged": { 31 | "*.{js,json,md}": [ 32 | "prettier --write" 33 | ] 34 | }, 35 | "files": [ 36 | "lib", 37 | "index.d.ts" 38 | ], 39 | "keywords": [ 40 | "react", 41 | "styled-components", 42 | "styled", 43 | "css", 44 | "css-moduels", 45 | "css-module" 46 | ], 47 | "dependencies": { 48 | "warning": "^4.0.3" 49 | }, 50 | "devDependencies": { 51 | "@babel/cli": "^7.8.4", 52 | "@babel/core": "^7.8.7", 53 | "@babel/plugin-proposal-class-properties": "^7.8.3", 54 | "@babel/plugin-proposal-object-rest-spread": "^7.8.3", 55 | "@babel/preset-env": "^7.8.7", 56 | "@babel/preset-flow": "^7.8.3", 57 | "@babel/preset-react": "^7.8.3", 58 | "@types/react": "^16.9.23", 59 | "all-contributors-cli": "^6.14.0", 60 | "babel-core": "7.0.0-bridge.0", 61 | "babel-jest": "^25.1.0", 62 | "enzyme": "^3.11.0", 63 | "enzyme-adapter-react-16": "^1.15.2", 64 | "eslint": "6.8.0", 65 | "eslint-config-precure": "^5.3.1", 66 | "flow-bin": "^0.108.0", 67 | "flow-copy-source": "^2.0.9", 68 | "husky": "^4.2.3", 69 | "jest": "^25.1.0", 70 | "jest-cli": "^25.1.0", 71 | "lint-staged": "^10.0.8", 72 | "prettier": "^1.19.1", 73 | "react": "^16.13.0", 74 | "react-dom": "^16.13.0", 75 | "rollup": "^2.1.0", 76 | "rollup-plugin-babel": "^4.4.0", 77 | "rollup-plugin-flow": "^1.1.1", 78 | "rollup-plugin-node-resolve": "^5.2.0" 79 | }, 80 | "peerDependencies": { 81 | "react": ">=15" 82 | }, 83 | "jest": { 84 | "setupFiles": [ 85 | "raf/polyfill", 86 | "/jestSetup.js" 87 | ], 88 | "modulePathIgnorePatterns": [ 89 | "/lib" 90 | ] 91 | }, 92 | "husky": { 93 | "hooks": { 94 | "pre-commit": "lint-staged" 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # styled-style 2 | 3 | [![Build Status](https://travis-ci.org/akameco/styled-style.svg?branch=master)](https://travis-ci.org/akameco/styled-style) 4 | [![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest) 5 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 6 | [![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors) 7 | 8 | > css modules like styled-components 9 | 10 | ## Why? 11 | 12 | [styled-components](https://www.styled-components.com/) are wonderful!!! 13 | However, if there are many existing css, it can not be transferred immediately. 14 | [styled-style](https://github.com/akameco/styled-style) can be transferred step by step. 15 | You can use readable components immediately. 16 | 17 | ## Install 18 | 19 | ``` 20 | $ yarn add styled-style 21 | ``` 22 | 23 | ## Usage 24 | 25 | styles.module.css 26 | 27 | ```css 28 | .center { 29 | display: flex; 30 | justify-content: center; 31 | align-items: center; 32 | } 33 | 34 | .btn { 35 | border-radius: 3px; 36 | padding: 0.25em 1em; 37 | margin: 0 1em; 38 | background: transparent; 39 | color: palevioletred; 40 | border: 2px solid palevioletred; 41 | } 42 | 43 | .primary { 44 | background: palevioletred; 45 | color: white; 46 | } 47 | ``` 48 | 49 | ```js 50 | import { styledStyle } from 'styled-style' 51 | import styles from './styles.module.css' 52 | 53 | const { div, button } = styledStyle(styles) 54 | 55 | const Center = div('center') 56 | const Button = button('btn') 57 | const PrimaryButton = button(['btn', 'primary']) 58 | 59 | render( 60 |

61 | 62 | Primary Button 63 |
64 | ) 65 | ``` 66 | 67 | ### Passed props 68 | 69 | ```js 70 | const Button = button(['btn', p => p.color === 'primary' && 'primary']) 71 | 72 | render( 73 |
74 | 75 | 76 |
77 | ) 78 | ``` 79 | 80 | ## API 81 | 82 | ### `styledStyle(styles: {[key: string]: string})` 83 | 84 | ## Examples 85 | 86 | [akameco/styled-style-example](https://github.com/akameco/styled-style-example) 87 | 88 | ## Contributors 89 | 90 | Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds/all-contributors#emoji-key)): 91 | 92 | 93 | 94 |
akameco
akameco

💻 📖 ⚠️ 🚇
純

💻
95 | 96 | 97 | 98 | This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! 99 | 100 | ## License 101 | 102 | MIT © [akameco](http://akameco.github.io) 103 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel' 2 | import flow from 'rollup-plugin-flow' 3 | import nodeResolve from 'rollup-plugin-node-resolve' 4 | import pkg from './package.json' 5 | 6 | const plugins = [ 7 | flow({ pretty: true }), 8 | nodeResolve(), 9 | babel({ exclude: 'node_modules/**' }), 10 | ] 11 | 12 | export default { 13 | plugins, 14 | input: 'src/index.js', 15 | external: ['react', 'warning'], 16 | output: [ 17 | { 18 | file: pkg.module, 19 | format: 'es', 20 | exports: 'named', 21 | globals: { react: 'React' }, 22 | }, 23 | { 24 | file: pkg.main, 25 | format: 'cjs', 26 | globals: { react: 'React' }, 27 | }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /src/elements.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export default [ 4 | 'a', 5 | 'abbr', 6 | 'address', 7 | 'area', 8 | 'article', 9 | 'aside', 10 | 'audio', 11 | 'b', 12 | 'base', 13 | 'bdi', 14 | 'bdo', 15 | 'big', 16 | 'blockquote', 17 | 'body', 18 | 'br', 19 | 'button', 20 | 'canvas', 21 | 'caption', 22 | 'cite', 23 | 'code', 24 | 'col', 25 | 'colgroup', 26 | 'data', 27 | 'datalist', 28 | 'dd', 29 | 'del', 30 | 'details', 31 | 'dfn', 32 | 'dialog', 33 | 'div', 34 | 'dl', 35 | 'dt', 36 | 'em', 37 | 'embed', 38 | 'fieldset', 39 | 'figcaption', 40 | 'figure', 41 | 'footer', 42 | 'form', 43 | 'h1', 44 | 'h2', 45 | 'h3', 46 | 'h4', 47 | 'h5', 48 | 'h6', 49 | 'head', 50 | 'header', 51 | 'hgroup', 52 | 'hr', 53 | 'html', 54 | 'i', 55 | 'iframe', 56 | 'img', 57 | 'input', 58 | 'ins', 59 | 'kbd', 60 | 'keygen', 61 | 'label', 62 | 'legend', 63 | 'li', 64 | 'link', 65 | 'main', 66 | 'map', 67 | 'mark', 68 | 'marquee', 69 | 'menu', 70 | 'menuitem', 71 | 'meta', 72 | 'meter', 73 | 'nav', 74 | 'noscript', 75 | 'object', 76 | 'ol', 77 | 'optgroup', 78 | 'option', 79 | 'output', 80 | 'p', 81 | 'param', 82 | 'picture', 83 | 'pre', 84 | 'progress', 85 | 'q', 86 | 'rp', 87 | 'rt', 88 | 'ruby', 89 | 's', 90 | 'samp', 91 | 'script', 92 | 'section', 93 | 'select', 94 | 'small', 95 | 'source', 96 | 'span', 97 | 'strong', 98 | 'style', 99 | 'sub', 100 | 'summary', 101 | 'sup', 102 | 'table', 103 | 'tbody', 104 | 'td', 105 | 'textarea', 106 | 'tfoot', 107 | 'th', 108 | 'thead', 109 | 'time', 110 | 'title', 111 | 'tr', 112 | 'track', 113 | 'u', 114 | 'ul', 115 | 'var', 116 | 'video', 117 | 'wbr', 118 | 119 | // SVG 120 | 'circle', 121 | 'clipPath', 122 | 'defs', 123 | 'ellipse', 124 | 'g', 125 | 'image', 126 | 'line', 127 | 'linearGradient', 128 | 'mask', 129 | 'path', 130 | 'pattern', 131 | 'polygon', 132 | 'polyline', 133 | 'radialGradient', 134 | 'rect', 135 | 'stop', 136 | 'svg', 137 | 'text', 138 | 'tspan', 139 | ] 140 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import * as React from 'react' 3 | import warning from 'warning' 4 | import elements from './elements' 5 | 6 | type Styles = { [key: string]: string } 7 | type _Selector = string | ((props: *) => *) 8 | type Selector = string | Array<_Selector> 9 | 10 | const createStyleElement = (element: string) => (styles: Styles) => ( 11 | selector: Selector 12 | ) => { 13 | const display = [] 14 | .concat(selector) 15 | .filter(v => typeof v === 'string') 16 | .join(' ') 17 | 18 | // eslint-disable-next-line flowtype/no-weak-types 19 | function createClassname(_selector: Selector, rest: any): string { 20 | if (Array.isArray(_selector)) { 21 | return _selector 22 | .map(v => { 23 | if (typeof v === 'function') { 24 | const result = v(rest) 25 | return result && styles[result] 26 | } 27 | warning(styles[v], `.${v} selector not found in css file.`) 28 | return styles[v] 29 | }) 30 | .filter(Boolean) 31 | .join(' ') 32 | } 33 | 34 | warning(styles[_selector], `.${_selector} selector not found in css file.`) 35 | 36 | return styles[_selector] 37 | } 38 | 39 | return class extends React.Component<{ children?: React.Node }> { 40 | static displayName = `styled(${display})` 41 | render() { 42 | const { children, ...rest } = this.props 43 | const className = createClassname(selector, rest) 44 | 45 | return React.createElement(element, { className, ...rest }, children) 46 | } 47 | } 48 | } 49 | 50 | export const styledStyle = (styles: Styles) => 51 | elements.reduce((acc, el) => { 52 | acc[el] = createStyleElement(el)(styles) 53 | return acc 54 | }, {}) 55 | -------------------------------------------------------------------------------- /src/index.test.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import * as React from 'react' 3 | import { mount } from 'enzyme' 4 | import { styledStyle } from '.' 5 | 6 | let spy 7 | beforeEach(() => { 8 | spy = jest.spyOn(console, 'error').mockImplementation(x => x) 9 | }) 10 | afterEach(() => { 11 | spy.mockReset() 12 | spy.mockRestore() 13 | }) 14 | 15 | test('when single string', () => { 16 | const { div } = styledStyle({ center: 'css-module-center' }) 17 | const Center = div('center') 18 | const wrapper = mount(
) 19 | expect(wrapper.childAt(0).prop('className')).toBe('css-module-center') 20 | expect(wrapper.name()).toBe('styled(center)') 21 | }) 22 | 23 | test('when array', () => { 24 | const { div } = styledStyle({ 25 | button: 'css-button', 26 | primary: 'css-primary', 27 | }) 28 | const PrimaryButton = div([ 29 | 'button', 30 | 'primary', 31 | props => props.hidden && 'hidden', 32 | ]) 33 | const wrapper = mount() 34 | expect(wrapper.name()).toBe('styled(button primary)') 35 | expect(wrapper.childAt(0).prop('className')).toBe('css-button css-primary') 36 | }) 37 | 38 | test('with props', () => { 39 | const { div } = styledStyle({ 40 | btn: 'css-button', 41 | primary: 'css-primary', 42 | }) 43 | const Button = div(['btn', p => p.color === 'primary' && 'primary']) 44 | const wrapper = mount() 45 | expect(wrapper.name()).toBe('styled(btn)') 46 | expect(wrapper.childAt(0).prop('className')).toBe('css-button css-primary') 47 | }) 48 | 49 | test('with no props', () => { 50 | const { div } = styledStyle({ 51 | btn: 'css-button', 52 | }) 53 | const Button = div(['btn', p => p.color === 'primary' && 'primary']) 54 | const wrapper = mount() 55 | expect(wrapper.name()).toBe('styled(btn)') 56 | expect(wrapper.childAt(0).prop('className')).toBe('css-button') 57 | }) 58 | 59 | describe('warning', () => { 60 | it('when selector not found', () => { 61 | const { button } = styledStyle({}) 62 | const Button = button('btn') 63 | mount() 64 | expect(spy.mock.calls[0][0]).toContain( 65 | '.btn selector not found in css file.' 66 | ) 67 | }) 68 | 69 | it('when selectors not found', () => { 70 | const { div } = styledStyle({ c: 'c' }) 71 | const Comp = div(['a', 'b', 'c']) 72 | mount() 73 | expect(spy.mock.calls[0][0]).toContain('.a selector not found in css file.') 74 | }) 75 | }) 76 | --------------------------------------------------------------------------------