@openeventkit/event-site 2.1.12 → 2.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/jest.yml +15 -0
- package/package.json +4 -4
- package/src/content/site-settings/index.json +1 -1
- package/src/content/sponsors.json +1 -1
- package/src/reducers/__tests__/scheduleReducer.test.js +201 -0
- package/src/reducers/schedule-reducer.js +3 -2
- package/src/styles/colors.scss +6 -6
- package/src/utils/__test__/syncFilters.test.js +46 -0
- package/src/utils/schedule.js +13 -13
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
name: jest
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
jobs:
|
|
5
|
+
build:
|
|
6
|
+
runs-on: ubuntu-latest
|
|
7
|
+
steps:
|
|
8
|
+
- uses: actions/checkout@v3
|
|
9
|
+
with:
|
|
10
|
+
fetch-depth: 0
|
|
11
|
+
- uses: actions/setup-node@v3
|
|
12
|
+
with:
|
|
13
|
+
node-version: 18
|
|
14
|
+
- run: yarn install
|
|
15
|
+
- run: yarn test
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openeventkit/event-site",
|
|
3
3
|
"description": "Event Site",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.14",
|
|
5
5
|
"author": "Tipit LLC",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@emotion/server": "^11.11.0",
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
"autoprefixer": "10.4.14",
|
|
38
38
|
"awesome-bootstrap-checkbox": "^1.0.1",
|
|
39
39
|
"axios": "^0.19.2",
|
|
40
|
-
"babel-preset-gatsby": "^3.
|
|
40
|
+
"babel-preset-gatsby": "^3.14.0",
|
|
41
41
|
"browser-tabs-lock": "^1.2.15",
|
|
42
42
|
"buffer": "^6.0.3",
|
|
43
43
|
"bulma": "^0.9.0",
|
|
44
44
|
"chain-function": "^1.0.1",
|
|
45
45
|
"classnames": "^2.3.1",
|
|
46
|
-
"core-js": "^
|
|
46
|
+
"core-js": "^3.44.0",
|
|
47
47
|
"cross-env": "^7.0.3",
|
|
48
48
|
"crypto-js": "^4.1.1",
|
|
49
49
|
"decap-cms-app": "^3.1.10",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"font-awesome": "^4.7.0",
|
|
57
57
|
"formik": "^2.4.6",
|
|
58
58
|
"fs-extra": "^11.3.0",
|
|
59
|
-
"full-schedule-widget": "3.0.
|
|
59
|
+
"full-schedule-widget": "3.0.11",
|
|
60
60
|
"gatsby": "^5.13.5",
|
|
61
61
|
"gatsby-alias-imports": "^1.0.6",
|
|
62
62
|
"gatsby-plugin-decap-cms": "^4.0.4",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"favicon":{"asset":"icon.png"},"widgets":{"chat":{"enabled":true,"showQA":false,"showHelp":false,"defaultScope":"page"},"schedule":{"allowClick":true}},"identityProviderButtons":[{"buttonColor":"#082238","providerLabel":"Continue with FNid","providerLogo":"logo_fn.svg","providerLogoSize":27},{"buttonColor":"#0A66C2","providerLabel":"Sign in with LinkedIn","providerParam":"linkedin","providerLogo":"logo_linkedin.svg","providerLogoSize":18},{"buttonColor":"#000000","providerLabel":"Sign in with Apple","providerParam":"apple","providerLogoSize":17,"providerLogo":"logo_apple.svg"},{"buttonColor":"#1877F2","providerLabel":"Login with Facebook","providerParam":"facebook","providerLogo":"logo_facebook.svg","providerLogoSize":20}],"maintenanceMode":{"enabled":false,"title":"Site under maintenance","subtitle":"Please reload page shortly"},"staticJsonFilesBuildTime":[{"file":"src/data/summit.json","build_time":
|
|
1
|
+
{"favicon":{"asset":"icon.png"},"widgets":{"chat":{"enabled":true,"showQA":false,"showHelp":false,"defaultScope":"page"},"schedule":{"allowClick":true}},"identityProviderButtons":[{"buttonColor":"#082238","providerLabel":"Continue with FNid","providerLogo":"logo_fn.svg","providerLogoSize":27},{"buttonColor":"#0A66C2","providerLabel":"Sign in with LinkedIn","providerParam":"linkedin","providerLogo":"logo_linkedin.svg","providerLogoSize":18},{"buttonColor":"#000000","providerLabel":"Sign in with Apple","providerParam":"apple","providerLogoSize":17,"providerLogo":"logo_apple.svg"},{"buttonColor":"#1877F2","providerLabel":"Login with Facebook","providerParam":"facebook","providerLogo":"logo_facebook.svg","providerLogoSize":20}],"maintenanceMode":{"enabled":false,"title":"Site under maintenance","subtitle":"Please reload page shortly"},"staticJsonFilesBuildTime":[{"file":"src/data/summit.json","build_time":1753889474771},{"file":"src/data/events.json","build_time":1753889479166},{"file":"src/data/events.idx.json","build_time":1753889479169},{"file":"src/data/speakers.json","build_time":1753889480006},{"file":"src/data/speakers.idx.json","build_time":1753889480006},{"file":"src/content/sponsors.json","build_time":1753889480726},{"file":"src/data/voteable-presentations.json","build_time":1753889481772}],"lastBuild":1753889481773}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[
|
|
1
|
+
[]
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
jest.mock('data/summit.json', () => ({
|
|
2
|
+
time_zone_id: 'America/New_York'
|
|
3
|
+
}), { virtual: true });
|
|
4
|
+
|
|
5
|
+
import scheduleReducer from '../schedule-reducer';
|
|
6
|
+
import { getFilteredEvents } from '../../utils/schedule';
|
|
7
|
+
/**
|
|
8
|
+
* We want to verify that your scheduleReducer properly handles the action:
|
|
9
|
+
* { type: 'SCHED_CLEAR_FILTERS' }
|
|
10
|
+
* That means:
|
|
11
|
+
* - filters are reset to baseFilters
|
|
12
|
+
* - baseFilters is not mutated
|
|
13
|
+
* - A new list of events is generated by calling getFilteredEvents(...)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
mock getFilteredEvents
|
|
19
|
+
we are not testing that function , we want to test if the reducer calls it correctly.
|
|
20
|
+
*/
|
|
21
|
+
jest.mock('../../utils/schedule', () => {
|
|
22
|
+
const originalModule = jest.requireActual('../../utils/schedule');
|
|
23
|
+
return {
|
|
24
|
+
...originalModule,
|
|
25
|
+
preFilterEvents: jest.fn(() => ['event-1', 'event-2']),
|
|
26
|
+
getFilteredEvents: jest.fn(() => ['filtered-event-1', 'filtered-event-2']),
|
|
27
|
+
syncFilters: jest.fn((newF, _) => newF)
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('scheduleReducer - SCHED_CLEAR_FILTERS', () => {
|
|
32
|
+
/*
|
|
33
|
+
define a mock state for a schedule, simulating:
|
|
34
|
+
- 2 filters (track, level) with current selections
|
|
35
|
+
- baseFilters representing the "original empty state"
|
|
36
|
+
- 2 allEvents, and 1 already filtered event
|
|
37
|
+
*/
|
|
38
|
+
const initialState = {
|
|
39
|
+
allEvents: ['event1', 'event2'],
|
|
40
|
+
filters: {
|
|
41
|
+
track: { label: 'Track', order: 1, values: ['Dev'], options: ['Dev', 'Ops'] },
|
|
42
|
+
level: { label: 'Level', order: 2, values: ['Advanced'], options: ['Beginner', 'Advanced'] }
|
|
43
|
+
},
|
|
44
|
+
baseFilters: {
|
|
45
|
+
track: { label: 'Track', order: 1, values: [], options: ['Dev', 'Ops'] },
|
|
46
|
+
level: { label: 'Level', order: 2, values: [], options: ['Beginner', 'Advanced'] }
|
|
47
|
+
},
|
|
48
|
+
events: ['some-event'],
|
|
49
|
+
hide_past_events_with_show_always_on_schedule: false
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
it('should reset filters to baseFilters with cleared values', () => {
|
|
53
|
+
// dispatch action SCHED_CLEAR_FILTERS
|
|
54
|
+
const action = { type: 'SCHED_CLEAR_FILTERS' };
|
|
55
|
+
const newState = scheduleReducer(initialState, action);
|
|
56
|
+
|
|
57
|
+
// filters match baseFilters
|
|
58
|
+
expect(newState.filters).toEqual({
|
|
59
|
+
track: { label: 'Track', order: 1, values: [], options: ['Dev', 'Ops'] },
|
|
60
|
+
level: { label: 'Level', order: 2, values: [], options: ['Beginner', 'Advanced'] }
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// getFilteredEvents was called with correct arguments
|
|
64
|
+
expect(getFilteredEvents).toHaveBeenCalledWith(
|
|
65
|
+
initialState.allEvents,
|
|
66
|
+
newState.filters,
|
|
67
|
+
expect.any(String), // summitTimeZoneId
|
|
68
|
+
false
|
|
69
|
+
);
|
|
70
|
+
// events were updated from its return value
|
|
71
|
+
expect(newState.events).toEqual(['filtered-event-1', 'filtered-event-2']);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not mutate baseFilters object', () => {
|
|
75
|
+
const baseClone = JSON.parse(JSON.stringify(initialState.baseFilters));
|
|
76
|
+
scheduleReducer(initialState, { type: 'SCHED_CLEAR_FILTERS' });
|
|
77
|
+
|
|
78
|
+
expect(initialState.baseFilters).toEqual(baseClone); // ensure not mutated
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
describe('scheduleReducer - SCHED_UPDATE_FILTER', () => {
|
|
86
|
+
const initialState = {
|
|
87
|
+
allEvents: ['event1', 'event2'],
|
|
88
|
+
filters: {
|
|
89
|
+
track: { label: 'Track', order: 1, values: ['Dev'], options: ['Dev', 'Ops'] },
|
|
90
|
+
level: { label: 'Level', order: 2, values: ['Advanced'], options: ['Beginner', 'Advanced'] }
|
|
91
|
+
},
|
|
92
|
+
baseFilters: {
|
|
93
|
+
track: { label: 'Track', order: 1, values: [], options: ['Dev', 'Ops'] },
|
|
94
|
+
level: { label: 'Level', order: 2, values: [], options: ['Beginner', 'Advanced'] }
|
|
95
|
+
},
|
|
96
|
+
events: [],
|
|
97
|
+
hide_past_events_with_show_always_on_schedule: false
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
it('should update a single filter on SCHED_UPDATE_FILTER', () => {
|
|
101
|
+
const action = {
|
|
102
|
+
type: 'SCHED_UPDATE_FILTER',
|
|
103
|
+
payload: {
|
|
104
|
+
type: 'track',
|
|
105
|
+
values: ['Ops'],
|
|
106
|
+
hide_past_events_with_show_always_on_schedule: false
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const state = scheduleReducer(initialState, action);
|
|
111
|
+
|
|
112
|
+
expect(state.filters.track.values).toEqual(['Ops']);
|
|
113
|
+
expect(state.filters.level.values).toEqual(['Advanced']); // unchanged
|
|
114
|
+
expect(getFilteredEvents).toHaveBeenCalled();
|
|
115
|
+
expect(state.events).toEqual(['filtered-event-1', 'filtered-event-2']);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should replace filters and view on SCHED_UPDATE_FILTERS', () => {
|
|
119
|
+
const action = {
|
|
120
|
+
type: 'SCHED_UPDATE_FILTERS',
|
|
121
|
+
payload: {
|
|
122
|
+
view: 'list',
|
|
123
|
+
filters: {
|
|
124
|
+
topic: { label: 'Topic', values: ['AI'], options: ['AI', 'ML'] }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const state = scheduleReducer(initialState, action);
|
|
130
|
+
|
|
131
|
+
expect(state.view).toBe('list');
|
|
132
|
+
expect(state.filters.topic.values).toEqual(['AI']);
|
|
133
|
+
expect(state.events).toEqual(['filtered-event-1', 'filtered-event-2']);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should return the same state on unknown action', () => {
|
|
137
|
+
const action = { type: 'UNKNOWN_ACTION' };
|
|
138
|
+
const state = scheduleReducer(initialState, action);
|
|
139
|
+
expect(state).toBe(initialState);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
describe('scheduleReducer - SCHED_RELOAD_SCHED_DATA', () => {
|
|
145
|
+
// mock initial state
|
|
146
|
+
const initialState = {
|
|
147
|
+
filters: {},
|
|
148
|
+
baseFilters: {},
|
|
149
|
+
allEvents: [],
|
|
150
|
+
events: [],
|
|
151
|
+
timeFormat: '12h',
|
|
152
|
+
hide_past_events_with_show_always_on_schedule: false
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// mock action
|
|
156
|
+
const action = {
|
|
157
|
+
type: 'SCHED_RELOAD_SCHED_DATA',
|
|
158
|
+
payload: {
|
|
159
|
+
color_source: 'Track',
|
|
160
|
+
pre_filters: { topic: { values: ['Dev'] } },
|
|
161
|
+
all_events: ['raw-event-1', 'raw-event-2'],
|
|
162
|
+
filters: {
|
|
163
|
+
topic: {
|
|
164
|
+
label: 'Topic',
|
|
165
|
+
values: [],
|
|
166
|
+
options: ['Dev', 'Ops'],
|
|
167
|
+
order: 1
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
baseFilters: {
|
|
171
|
+
topic: {
|
|
172
|
+
label: 'Topic',
|
|
173
|
+
values: [],
|
|
174
|
+
options: ['Dev', 'Ops'],
|
|
175
|
+
order: 1
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
only_events_with_attendee_access: true,
|
|
179
|
+
hide_past_events_with_show_always_on_schedule: true,
|
|
180
|
+
is_my_schedule: true,
|
|
181
|
+
isLoggedUser: true,
|
|
182
|
+
userProfile: { id: 123 },
|
|
183
|
+
time_format: '24h'
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
it('should reload schedule data and compute new filtered events', () => {
|
|
188
|
+
|
|
189
|
+
// dispatch action
|
|
190
|
+
const newState = scheduleReducer(initialState, action);
|
|
191
|
+
|
|
192
|
+
expect(newState.allEvents).toEqual(['event-1', 'event-2']);
|
|
193
|
+
expect(newState.colorSource).toBe('track');
|
|
194
|
+
expect(newState.filters).toEqual(action.payload.filters);
|
|
195
|
+
expect(newState.baseFilters).toEqual(action.payload.baseFilters);
|
|
196
|
+
expect(newState.events).toEqual(['filtered-event-1', 'filtered-event-2']);
|
|
197
|
+
// This confirms that state.timeFormat is preserved from the previous state if already defined,
|
|
198
|
+
// even though 24h is in the payload.
|
|
199
|
+
expect(newState.timeFormat).toBe('12h'); // keeps existing unless null
|
|
200
|
+
});
|
|
201
|
+
});
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
preFilterEvents,
|
|
5
5
|
syncFilters
|
|
6
6
|
} from "../utils/schedule";
|
|
7
|
-
|
|
7
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
8
8
|
import summitData from "data/summit.json";
|
|
9
9
|
|
|
10
10
|
const summitTimeZoneId = summitData.time_zone_id; // TODO use reducer data
|
|
@@ -59,7 +59,8 @@ const scheduleReducer = (state = INITIAL_STATE, action) => {
|
|
|
59
59
|
return {
|
|
60
60
|
...state,
|
|
61
61
|
allEvents: allFilteredEvents,
|
|
62
|
-
baseFilters
|
|
62
|
+
// baseFilters is immutable
|
|
63
|
+
baseFilters: cloneDeep(baseFilters),
|
|
63
64
|
filters: newFilters,
|
|
64
65
|
colorSource: color_source.toLowerCase(),
|
|
65
66
|
events,
|
package/src/styles/colors.scss
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
$color_accent: #
|
|
1
|
+
$color_accent: #ff5e32;
|
|
2
2
|
$color_alerts: #ff0000;
|
|
3
3
|
$color_background_light: #ffffff;
|
|
4
4
|
$color_background_dark: #000000;
|
|
@@ -19,13 +19,13 @@ $color_input_text_color_light: #363636;
|
|
|
19
19
|
$color_input_text_color_dark: #ffffff;
|
|
20
20
|
$color_input_text_color_disabled_light: #ffffff;
|
|
21
21
|
$color_input_text_color_disabled_dark: #ffffff;
|
|
22
|
-
$color_primary: #
|
|
23
|
-
$color_primary_contrast: #
|
|
24
|
-
$color_secondary: #
|
|
25
|
-
$color_secondary_contrast: #
|
|
22
|
+
$color_primary: #6d6e71;
|
|
23
|
+
$color_primary_contrast: #f1f2f2;
|
|
24
|
+
$color_secondary: #00cec4;
|
|
25
|
+
$color_secondary_contrast: #ff5e32;
|
|
26
26
|
$color_text_light: #ffffff;
|
|
27
27
|
$color_text_med: #828282;
|
|
28
|
-
$color_text_dark: #
|
|
28
|
+
$color_text_dark: #333333;
|
|
29
29
|
$color_text_input_hints_light: #7b7b7b;
|
|
30
30
|
$color_text_input_hints_dark: #7b7b7b;
|
|
31
31
|
$color_text_input_hints: #c4c4c4;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { syncFilters } from '../schedule';
|
|
2
|
+
|
|
3
|
+
describe('syncFilters', () => {
|
|
4
|
+
|
|
5
|
+
const originalNewFilters = {
|
|
6
|
+
track: { label: 'Track', order: 1, values: ['A'], options: ['A'] },
|
|
7
|
+
level: { label: 'Level', order: 2, values: ['Beginner'], options: ['Beginner'] }
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const currentFilters = {
|
|
11
|
+
track: { values: ['Dev'], options: ['Dev', 'Ops'] },
|
|
12
|
+
level: { values: ['Intermediate'], options: ['Beginner', 'Advanced'] }
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
it('should return new filters with values/options copied from current filters', () => {
|
|
16
|
+
const newFilters = JSON.parse(JSON.stringify(originalNewFilters)); // deep clone
|
|
17
|
+
|
|
18
|
+
const result = syncFilters(newFilters, currentFilters);
|
|
19
|
+
|
|
20
|
+
expect(result.track.values).toEqual(['Dev']);
|
|
21
|
+
expect(result.track.options).toEqual(['Dev', 'Ops']);
|
|
22
|
+
expect(result.level.values).toEqual(['Intermediate']);
|
|
23
|
+
expect(result.level.options).toEqual(['Beginner', 'Advanced']);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should not mutate original newFilters object', () => {
|
|
27
|
+
const newFilters = JSON.parse(JSON.stringify(originalNewFilters)); // deep clone
|
|
28
|
+
|
|
29
|
+
syncFilters(newFilters, currentFilters);
|
|
30
|
+
|
|
31
|
+
expect(newFilters.track.values).toEqual(['A']);
|
|
32
|
+
expect(newFilters.track.options).toEqual(['A']);
|
|
33
|
+
expect(newFilters.level.values).toEqual(['Beginner']);
|
|
34
|
+
expect(newFilters.level.options).toEqual(['Beginner']);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should produce a result that does not share references with currentFilters', () => {
|
|
38
|
+
const newFilters = JSON.parse(JSON.stringify(originalNewFilters));
|
|
39
|
+
|
|
40
|
+
const result = syncFilters(newFilters, currentFilters);
|
|
41
|
+
|
|
42
|
+
expect(result.track.values).not.toBe(currentFilters.track.values);
|
|
43
|
+
expect(result.track.options).not.toBe(currentFilters.track.options);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
});
|
package/src/utils/schedule.js
CHANGED
|
@@ -191,21 +191,21 @@ export const getFilteredEvents = (events, filters, summitTimezone, hidePast) =>
|
|
|
191
191
|
};
|
|
192
192
|
|
|
193
193
|
export const syncFilters = (newFilters, currentFilters) => {
|
|
194
|
-
|
|
194
|
+
/*
|
|
195
|
+
new filters are the source of truth
|
|
196
|
+
returns a clone
|
|
197
|
+
*/
|
|
198
|
+
const synced = {};
|
|
199
|
+
|
|
195
200
|
Object.entries(newFilters).forEach(([key, value]) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if(filter.hasOwnProperty("values"))
|
|
203
|
-
value.values = filter.values;
|
|
204
|
-
if(filter.hasOwnProperty("options"))
|
|
205
|
-
value.options = filter.options;
|
|
206
|
-
}
|
|
201
|
+
const existing = currentFilters[key] || {};
|
|
202
|
+
synced[key] = {
|
|
203
|
+
...value,
|
|
204
|
+
values: Array.isArray(existing.values) ? [...existing.values] : [],
|
|
205
|
+
options: Array.isArray(existing.options) ? [...existing.options] : []
|
|
206
|
+
};
|
|
207
207
|
});
|
|
208
|
-
return
|
|
208
|
+
return synced;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
export const savePendingAction = (action) => {
|