Skip to content

Commit

Permalink
feat(store): preserve the event name case with createActionGroup
Browse files Browse the repository at this point in the history
Closes #3693

BREAKING CHANGES:

The event name case is preserved when converting to the action name by using the `createActionGroup` function.

BEFORE:

All letters of the event name will be lowercase, except for the initial letters of words starting from the second word, which will be uppercase.

```ts
const authApiActions = createActionGroup({
  source: 'Auth API',
  events: {
    'LogIn Success': emptyProps(),
    'login failure': emptyProps(),
    'Logout Success': emptyProps(),
    logoutFailure: emptyProps(),
  },
});

// generated actions:
const {
  loginSuccess,
  loginFailure,
  logoutSuccess,
  logoutfailure,
} = authApiActions;
```

AFTER:

The initial letter of the first word of the event name will be lowercase, and the initial letters of the other words will be uppercase. The case of other letters in the event name will remain the same.

```ts
const {
  logInSuccess,
  loginFailure,
  logoutSuccess,
  logoutFailure,
} = authApiActions;
```
  • Loading branch information
markostanimirovic committed Apr 25, 2023
1 parent 73eb55c commit 13972c2
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 11 deletions.
9 changes: 7 additions & 2 deletions modules/store/spec/action_group_creator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@ describe('createActionGroup', () => {
source: 'Books API',
events: {
' Load BOOKS suCCess ': emptyProps(),
loadBooksFailure: emptyProps(),
},
});

it('should create action name by camel casing the event name', () => {
expect(booksApiActions.loadBooksSuccess).toBeDefined();
expect(booksApiActions.loadBOOKSSuCCess).toBeDefined();
expect(booksApiActions.loadBooksFailure).toBeDefined();
});

it('should create action type using the "[Source] Event" pattern', () => {
expect(booksApiActions.loadBooksSuccess().type).toBe(
expect(booksApiActions.loadBOOKSSuCCess().type).toBe(
'[Books API] Load BOOKS suCCess '
);
expect(booksApiActions.loadBooksFailure().type).toBe(
'[Books API] loadBooksFailure'
);
});

it('should create action with props', () => {
Expand Down
12 changes: 11 additions & 1 deletion modules/store/spec/helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { capitalize } from '../src/helpers';
import { capitalize, uncapitalize } from '../src/helpers';

describe('helpers', () => {
describe('capitalize', () => {
Expand All @@ -10,4 +10,14 @@ describe('helpers', () => {
expect(capitalize('')).toEqual('');
});
});

describe('uncapitalize', () => {
it('should uncapitalize the text', () => {
expect(uncapitalize('NGRX')).toEqual('nGRX');
});

it('should return an empty string when the text is an empty string', () => {
expect(uncapitalize('')).toEqual('');
});
});
});
19 changes: 15 additions & 4 deletions modules/store/spec/types/action_group_creator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,22 +98,33 @@ describe('createActionGroup', () => {

describe('event name', () => {
it('should create action name by camel casing the event name', () => {
expectSnippet(`
const snippet = expectSnippet(`
const booksApiActions = createActionGroup({
source: 'Books API',
events: {
' Load BOOKS suCCess ': emptyProps(),
loadBooksFailure: emptyProps(),
},
});
let loadBooksSuccess: typeof booksApiActions.loadBooksSuccess;
`).toInfer(
let loadBooksSuccess: typeof booksApiActions.loadBOOKSSuCCess;
let loadBooksFailure: typeof booksApiActions.loadBooksFailure;
`);

snippet.toInfer(
'loadBooksSuccess',
`ActionCreator<
"[Books API] Load BOOKS suCCess ",
() => TypedAction<"[Books API] Load BOOKS suCCess ">
>`
);
snippet.toInfer(
'loadBooksFailure',
`ActionCreator<
"[Books API] loadBooksFailure",
() => TypedAction<"[Books API] loadBooksFailure">
>`
);
});

it('should fail when event name is an empty string', () => {
Expand Down Expand Up @@ -222,7 +233,7 @@ describe('createActionGroup', () => {
const booksApiActions = createActionGroup({
source: 'Books API',
events: {
' Load BOOks success ': emptyProps(),
' Load Books success ': emptyProps(),
'load Books Success': props<{ books: string[] }>(),
}
});
Expand Down
5 changes: 2 additions & 3 deletions modules/store/src/action_group_creator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createAction, props } from './action_creator';
import { ActionCreatorProps, Creator } from './models';
import { capitalize } from './helpers';
import { capitalize, uncapitalize } from './helpers';
import {
ActionGroup,
ActionGroupConfig,
Expand Down Expand Up @@ -75,9 +75,8 @@ function toActionName<EventName extends string>(
): ActionName<EventName> {
return eventName
.trim()
.toLowerCase()
.split(' ')
.map((word, i) => (i === 0 ? word : capitalize(word)))
.map((word, i) => (i === 0 ? uncapitalize(word) : capitalize(word)))
.join('') as ActionName<EventName>;
}

Expand Down
2 changes: 1 addition & 1 deletion modules/store/src/action_group_creator_models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ type EventCreator<
: never;

export type ActionName<EventName extends string> = Uncapitalize<
Join<TitleCase<Lowercase<Trim<EventName>>>>
Join<TitleCase<EventName>>
>;

export interface ActionGroupConfig<
Expand Down
4 changes: 4 additions & 0 deletions modules/store/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export function capitalize<T extends string>(text: T): Capitalize<T> {
return (text.charAt(0).toUpperCase() + text.substring(1)) as Capitalize<T>;
}

export function uncapitalize<T extends string>(text: T): Uncapitalize<T> {
return (text.charAt(0).toLowerCase() + text.substring(1)) as Uncapitalize<T>;
}

0 comments on commit 13972c2

Please sign in to comment.