volto-aemet 1.0.0-alpha.0
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/.changelog.draft +3 -0
- package/.release-it.json +34 -0
- package/CHANGELOG.md +11 -0
- package/LICENSE +21 -0
- package/README.md +323 -0
- package/babel.config.js +17 -0
- package/locales/de/LC_MESSAGES/volto.po +83 -0
- package/locales/en/LC_MESSAGES/volto.po +83 -0
- package/locales/es/LC_MESSAGES/volto.po +90 -0
- package/locales/pt_BR/LC_MESSAGES/volto.po +88 -0
- package/locales/volto.pot +85 -0
- package/news/.gitkeep +0 -0
- package/package.json +47 -0
- package/public/.gitkeep +0 -0
- package/src/components/Aemet/Weather.js +289 -0
- package/src/components/Aemet/Weather.test.js +398 -0
- package/src/components/Aemet/WeatherCurrent.jsx +97 -0
- package/src/components/Aemet/WeatherCurrent.test.jsx +282 -0
- package/src/components/Aemet/index.js +9 -0
- package/src/components/Blocks/AemetWeatherCurrent/View.jsx +11 -0
- package/src/components/Blocks/AemetWeatherForecast/Edit.jsx +72 -0
- package/src/components/Blocks/AemetWeatherForecast/View.jsx +32 -0
- package/src/components/Blocks/AemetWeatherForecast/messages.js +36 -0
- package/src/components/Blocks/AemetWeatherForecast/schema.js +37 -0
- package/src/config/blocks.ts +70 -0
- package/src/config/settings.ts +16 -0
- package/src/icons/aemet-logo.svg +311 -0
- package/src/icons/weather/temps/11.png +0 -0
- package/src/icons/weather/temps/11n.png +0 -0
- package/src/icons/weather/temps/12.png +0 -0
- package/src/icons/weather/temps/12n.png +0 -0
- package/src/icons/weather/temps/13.png +0 -0
- package/src/icons/weather/temps/13n.png +0 -0
- package/src/icons/weather/temps/14.png +0 -0
- package/src/icons/weather/temps/14n.png +0 -0
- package/src/icons/weather/temps/15.png +0 -0
- package/src/icons/weather/temps/15n.png +0 -0
- package/src/icons/weather/temps/16.png +0 -0
- package/src/icons/weather/temps/16n.png +0 -0
- package/src/icons/weather/temps/17.png +0 -0
- package/src/icons/weather/temps/17n.png +0 -0
- package/src/icons/weather/temps/23.png +0 -0
- package/src/icons/weather/temps/23n.png +0 -0
- package/src/icons/weather/temps/24.png +0 -0
- package/src/icons/weather/temps/24n.png +0 -0
- package/src/icons/weather/temps/25.png +0 -0
- package/src/icons/weather/temps/25n.png +0 -0
- package/src/icons/weather/temps/26.png +0 -0
- package/src/icons/weather/temps/26n.png +0 -0
- package/src/icons/weather/temps/33.png +0 -0
- package/src/icons/weather/temps/33n.png +0 -0
- package/src/icons/weather/temps/34.png +0 -0
- package/src/icons/weather/temps/34n.png +0 -0
- package/src/icons/weather/temps/35.png +0 -0
- package/src/icons/weather/temps/35n.png +0 -0
- package/src/icons/weather/temps/36.png +0 -0
- package/src/icons/weather/temps/36n.png +0 -0
- package/src/icons/weather/temps/43.png +0 -0
- package/src/icons/weather/temps/43n.png +0 -0
- package/src/icons/weather/temps/44.png +0 -0
- package/src/icons/weather/temps/44n.png +0 -0
- package/src/icons/weather/temps/45.png +0 -0
- package/src/icons/weather/temps/45n.png +0 -0
- package/src/icons/weather/temps/46.png +0 -0
- package/src/icons/weather/temps/46n.png +0 -0
- package/src/icons/weather/temps/51.png +0 -0
- package/src/icons/weather/temps/51n.png +0 -0
- package/src/icons/weather/temps/52.png +0 -0
- package/src/icons/weather/temps/52n.png +0 -0
- package/src/icons/weather/temps/53.png +0 -0
- package/src/icons/weather/temps/54.png +0 -0
- package/src/icons/weather/temps/54n.png +0 -0
- package/src/icons/weather/temps/61.png +0 -0
- package/src/icons/weather/temps/61n.png +0 -0
- package/src/icons/weather/temps/62.png +0 -0
- package/src/icons/weather/temps/62n.png +0 -0
- package/src/icons/weather/temps/63.png +0 -0
- package/src/icons/weather/temps/63n.png +0 -0
- package/src/icons/weather/temps/64.png +0 -0
- package/src/icons/weather/temps/64n.png +0 -0
- package/src/icons/weather/temps/71.png +0 -0
- package/src/icons/weather/temps/71n.png +0 -0
- package/src/icons/weather/temps/72.png +0 -0
- package/src/icons/weather/temps/72n.png +0 -0
- package/src/icons/weather/temps/73.png +0 -0
- package/src/icons/weather/temps/74.png +0 -0
- package/src/index.ts +24 -0
- package/src/theme/components/_weather_current.scss +69 -0
- package/src/theme/main.scss +3 -0
- package/towncrier.toml +33 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for WeatherCurrent.jsx view component.
|
|
3
|
+
* @module components/Aemet/WeatherCurrent.test
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { render, screen } from '@testing-library/react';
|
|
8
|
+
import { IntlProvider } from 'react-intl';
|
|
9
|
+
import { useWeatherData, getWeatherIcon } from './Weather.js';
|
|
10
|
+
import WeatherCurrent from './WeatherCurrent.jsx';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Mocks (hoisted by babel-jest before imports)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
jest.mock('./Weather.js', () => ({
|
|
17
|
+
weatherMessages: {
|
|
18
|
+
minimumMaximum: {
|
|
19
|
+
id: 'minimum_maximum',
|
|
20
|
+
defaultMessage: 'Minimum-Maximum',
|
|
21
|
+
},
|
|
22
|
+
currentDay: { id: 'current_day', defaultMessage: 'Current day' },
|
|
23
|
+
currentLocation: {
|
|
24
|
+
id: 'current_location',
|
|
25
|
+
defaultMessage: 'Current location',
|
|
26
|
+
},
|
|
27
|
+
currentTemperature: {
|
|
28
|
+
id: 'current_temperature',
|
|
29
|
+
defaultMessage: 'Current temperature',
|
|
30
|
+
},
|
|
31
|
+
currentTime: { id: 'current_time', defaultMessage: 'Current time' },
|
|
32
|
+
loadingCurrent: {
|
|
33
|
+
id: 'loading_weather_forecast',
|
|
34
|
+
defaultMessage: 'Loading weather forecast...',
|
|
35
|
+
},
|
|
36
|
+
weatherNotAvailable: {
|
|
37
|
+
id: 'not_available',
|
|
38
|
+
defaultMessage: 'Not available',
|
|
39
|
+
},
|
|
40
|
+
weatherDescription: {
|
|
41
|
+
id: 'weather_description',
|
|
42
|
+
defaultMessage: 'Weather description',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
getWeatherIcon: jest.fn(() => 'mocked-icon.png'),
|
|
46
|
+
useWeatherData: jest.fn(),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
jest.mock('@plone/volto/components/theme/Image/Image', () => {
|
|
50
|
+
const MockImage = ({ src, alt }) => <img src={src} alt={alt} />;
|
|
51
|
+
return MockImage;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Fix moment to a predictable point in time so clock assertions are stable.
|
|
55
|
+
jest.mock('moment', () => {
|
|
56
|
+
const fixedTime = {
|
|
57
|
+
format: jest.fn((pattern) => {
|
|
58
|
+
if (pattern === 'HH:mm') return '14:30';
|
|
59
|
+
if (pattern === 'ddd') return 'Fri';
|
|
60
|
+
if (pattern === 'YYYY-MM-DD') return '2024-01-15';
|
|
61
|
+
return '';
|
|
62
|
+
}),
|
|
63
|
+
};
|
|
64
|
+
return jest.fn(() => fixedTime);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Test data
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
const MOCK_DAY = {
|
|
72
|
+
currentHour: '14',
|
|
73
|
+
date: '2024-01-15',
|
|
74
|
+
name: 'Fuenlabrada',
|
|
75
|
+
provinceName: 'Madrid',
|
|
76
|
+
skyStateDescription: 'Clear sky',
|
|
77
|
+
skyStateText: '11',
|
|
78
|
+
minTemp: '18',
|
|
79
|
+
maxTemp: '33',
|
|
80
|
+
timePeriod: '12-18',
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Helpers
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
const renderComponent = () =>
|
|
88
|
+
render(
|
|
89
|
+
<IntlProvider locale="en" messages={{}}>
|
|
90
|
+
<WeatherCurrent />
|
|
91
|
+
</IntlProvider>,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Tests
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
describe('WeatherCurrent', () => {
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
// clearAllMocks preserves mock implementations (including the moment factory)
|
|
101
|
+
// while still resetting call counts. resetAllMocks would strip the
|
|
102
|
+
// jest.fn(() => fixedTime) factory, making moment() return undefined.
|
|
103
|
+
jest.clearAllMocks();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// -------------------------------------------------------------------------
|
|
107
|
+
describe('loading state', () => {
|
|
108
|
+
it('shows the loading message when isLoading is true', () => {
|
|
109
|
+
useWeatherData.mockReturnValue({ forecast: [], isLoading: true });
|
|
110
|
+
renderComponent();
|
|
111
|
+
expect(screen.getByText(/Loading weather forecast/i)).toBeInTheDocument();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('shows the loading message when forecast array is empty', () => {
|
|
115
|
+
useWeatherData.mockReturnValue({ forecast: [], isLoading: false });
|
|
116
|
+
renderComponent();
|
|
117
|
+
expect(screen.getByText(/Loading weather forecast/i)).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('does not render the forecast card while loading', () => {
|
|
121
|
+
useWeatherData.mockReturnValue({ forecast: [], isLoading: true });
|
|
122
|
+
const { container } = renderComponent();
|
|
123
|
+
expect(
|
|
124
|
+
container.querySelector('.weather-current-card'),
|
|
125
|
+
).not.toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
describe('data state', () => {
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
useWeatherData.mockReturnValue({
|
|
133
|
+
forecast: [MOCK_DAY],
|
|
134
|
+
isLoading: false,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('does not show the loading message when data is available', () => {
|
|
139
|
+
renderComponent();
|
|
140
|
+
expect(
|
|
141
|
+
screen.queryByText(/Loading weather forecast/i),
|
|
142
|
+
).not.toBeInTheDocument();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('renders the current time in HH:mm format', () => {
|
|
146
|
+
renderComponent();
|
|
147
|
+
expect(screen.getByText('14:30')).toBeInTheDocument();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('renders the day abbreviation in uppercase', () => {
|
|
151
|
+
renderComponent();
|
|
152
|
+
expect(screen.getByText('FRI')).toBeInTheDocument();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('renders the name', () => {
|
|
156
|
+
renderComponent();
|
|
157
|
+
expect(screen.getByText(/Fuenlabrada/)).toBeInTheDocument();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('renders the province name', () => {
|
|
161
|
+
renderComponent();
|
|
162
|
+
expect(screen.getByText(/Madrid/)).toBeInTheDocument();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('renders the temperature range (min-max)', () => {
|
|
166
|
+
renderComponent();
|
|
167
|
+
expect(screen.getByText('18-33\u00ba')).toBeInTheDocument();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('renders the maximum temperature', () => {
|
|
171
|
+
renderComponent();
|
|
172
|
+
expect(screen.getByText('33\u00ba')).toBeInTheDocument();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('renders the weather icon with sky state description as alt text', () => {
|
|
176
|
+
renderComponent();
|
|
177
|
+
expect(screen.getByRole('img')).toHaveAttribute('alt', 'Clear sky');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('sets the sky state description as the icon wrapper title', () => {
|
|
181
|
+
const { container } = renderComponent();
|
|
182
|
+
expect(container.querySelector('.weather-current-icon')).toHaveAttribute(
|
|
183
|
+
'title',
|
|
184
|
+
'Clear sky',
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('calls getWeatherIcon with the correct sky state text', () => {
|
|
189
|
+
renderComponent();
|
|
190
|
+
expect(getWeatherIcon).toHaveBeenCalledWith('11');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('sets the currentTime title on the time span', () => {
|
|
194
|
+
const { container } = renderComponent();
|
|
195
|
+
expect(container.querySelector('.weather-current-time')).toHaveAttribute(
|
|
196
|
+
'title',
|
|
197
|
+
'Current time',
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('sets the currentDay title on the day span', () => {
|
|
202
|
+
const { container } = renderComponent();
|
|
203
|
+
expect(container.querySelector('.weather-current-day')).toHaveAttribute(
|
|
204
|
+
'title',
|
|
205
|
+
'Current day',
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('sets the currentLocation title on the city div', () => {
|
|
210
|
+
const { container } = renderComponent();
|
|
211
|
+
expect(container.querySelector('.weather-current-city')).toHaveAttribute(
|
|
212
|
+
'title',
|
|
213
|
+
'Current location',
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// -------------------------------------------------------------------------
|
|
219
|
+
describe('fallbacks', () => {
|
|
220
|
+
it('still renders province when name is missing', () => {
|
|
221
|
+
useWeatherData.mockReturnValue({
|
|
222
|
+
forecast: [{ ...MOCK_DAY, name: '' }],
|
|
223
|
+
isLoading: false,
|
|
224
|
+
});
|
|
225
|
+
const { container } = renderComponent();
|
|
226
|
+
expect(
|
|
227
|
+
container.querySelector('.weather-current-city').textContent,
|
|
228
|
+
).toContain('Madrid');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('still renders name when provinceName is missing', () => {
|
|
232
|
+
useWeatherData.mockReturnValue({
|
|
233
|
+
forecast: [{ ...MOCK_DAY, provinceName: '' }],
|
|
234
|
+
isLoading: false,
|
|
235
|
+
});
|
|
236
|
+
const { container } = renderComponent();
|
|
237
|
+
expect(
|
|
238
|
+
container.querySelector('.weather-current-city').textContent,
|
|
239
|
+
).toContain('Fuenlabrada');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('uses the weatherDescription default message as icon title when skyStateDescription is absent', () => {
|
|
243
|
+
useWeatherData.mockReturnValue({
|
|
244
|
+
forecast: [{ ...MOCK_DAY, skyStateDescription: '' }],
|
|
245
|
+
isLoading: false,
|
|
246
|
+
});
|
|
247
|
+
const { container } = renderComponent();
|
|
248
|
+
expect(container.querySelector('.weather-current-icon')).toHaveAttribute(
|
|
249
|
+
'title',
|
|
250
|
+
'Weather description',
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// -------------------------------------------------------------------------
|
|
256
|
+
describe('clock timer', () => {
|
|
257
|
+
beforeEach(() => {
|
|
258
|
+
jest.useFakeTimers();
|
|
259
|
+
useWeatherData.mockReturnValue({
|
|
260
|
+
forecast: [MOCK_DAY],
|
|
261
|
+
isLoading: false,
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
afterEach(() => {
|
|
266
|
+
jest.useRealTimers();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('registers a 60-second interval for the live clock', () => {
|
|
270
|
+
const spy = jest.spyOn(global, 'setInterval');
|
|
271
|
+
renderComponent();
|
|
272
|
+
expect(spy).toHaveBeenCalledWith(expect.any(Function), 60000);
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('clears the interval on unmount to prevent memory leaks', () => {
|
|
276
|
+
const clearSpy = jest.spyOn(global, 'clearInterval');
|
|
277
|
+
const { unmount } = renderComponent();
|
|
278
|
+
unmount();
|
|
279
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aemet Weather Current View mode.
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import WeatherCurrent from 'volto-aemet/components/Aemet';
|
|
6
|
+
|
|
7
|
+
const AemetWeatherCurrentView = (props) => {
|
|
8
|
+
return <WeatherCurrent />;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default AemetWeatherCurrentView;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aemet Weather Forecast Edit mode.
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import cx from 'classnames';
|
|
6
|
+
import messages from './messages';
|
|
7
|
+
import SidebarPortal from '@plone/volto/components/manage/Sidebar/SidebarPortal';
|
|
8
|
+
import { BlockDataForm } from '@plone/volto/components/manage/Form';
|
|
9
|
+
import { useIntl } from 'react-intl';
|
|
10
|
+
|
|
11
|
+
const AemetWeatherForecastEdit = (props) => {
|
|
12
|
+
const {
|
|
13
|
+
block,
|
|
14
|
+
data,
|
|
15
|
+
style,
|
|
16
|
+
className,
|
|
17
|
+
blocksConfig,
|
|
18
|
+
selected,
|
|
19
|
+
onChangeBlock,
|
|
20
|
+
navRoot,
|
|
21
|
+
contentType,
|
|
22
|
+
blocksErrors,
|
|
23
|
+
} = props;
|
|
24
|
+
const intl = useIntl();
|
|
25
|
+
|
|
26
|
+
const schema = blocksConfig['aemetWeatherForecast'].blockSchema({
|
|
27
|
+
props,
|
|
28
|
+
intl,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
<div style={style} className={cx('block handson', { className })}>
|
|
34
|
+
<div className="block-inner-container">
|
|
35
|
+
<iframe
|
|
36
|
+
name="iframe_aemet_id33044"
|
|
37
|
+
title={intl.formatMessage(messages.aemetWeatherForecastBlock)}
|
|
38
|
+
width="100%"
|
|
39
|
+
height="100%"
|
|
40
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
41
|
+
tabindex="0"
|
|
42
|
+
id="iframe_aemet_id33044"
|
|
43
|
+
src={`https://www.aemet.es/es/eltiempo/prediccion/municipios/mostrarwidget/${data.province || 'sevilla'}-id${data.location_id || '41091'}?w=g4p011100001ohmffffffx4f86d9t95b6e9r1s8n2`}
|
|
44
|
+
frameborder="0"
|
|
45
|
+
scrolling="no"
|
|
46
|
+
></iframe>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<SidebarPortal selected={selected}>
|
|
50
|
+
<BlockDataForm
|
|
51
|
+
schema={schema}
|
|
52
|
+
title={schema.title}
|
|
53
|
+
onChangeField={(id, value) => {
|
|
54
|
+
onChangeBlock(block, {
|
|
55
|
+
...data,
|
|
56
|
+
[id]: value,
|
|
57
|
+
});
|
|
58
|
+
}}
|
|
59
|
+
onChangeBlock={onChangeBlock}
|
|
60
|
+
formData={data}
|
|
61
|
+
block={block}
|
|
62
|
+
blocksConfig={blocksConfig}
|
|
63
|
+
navRoot={navRoot}
|
|
64
|
+
contentType={contentType}
|
|
65
|
+
errors={blocksErrors}
|
|
66
|
+
/>
|
|
67
|
+
</SidebarPortal>
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default AemetWeatherForecastEdit;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aemet Weather Forecast View mode.
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import cx from 'classnames';
|
|
7
|
+
import messages from './messages';
|
|
8
|
+
|
|
9
|
+
const AemetWeatherForecastView = (props) => {
|
|
10
|
+
const intl = useIntl();
|
|
11
|
+
const { data, style, className } = props;
|
|
12
|
+
return (
|
|
13
|
+
<div style={style} className={cx('block handson', { className })}>
|
|
14
|
+
<div className="block-inner-container">
|
|
15
|
+
<iframe
|
|
16
|
+
name="iframe_aemet_id33044"
|
|
17
|
+
title={intl.formatMessage(messages.aemetWeatherForecastBlock)}
|
|
18
|
+
width="100%"
|
|
19
|
+
height="100%"
|
|
20
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
21
|
+
tabindex="0"
|
|
22
|
+
id="iframe_aemet_id33044"
|
|
23
|
+
src={`https://www.aemet.es/es/eltiempo/prediccion/municipios/mostrarwidget/${data.province || 'sevilla'}-id${data.location_id || '41091'}?w=g4p011100001ohmffffffx4f86d9t95b6e9r1s8n2`}
|
|
24
|
+
frameborder="0"
|
|
25
|
+
scrolling="no"
|
|
26
|
+
></iframe>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default AemetWeatherForecastView;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aemet Weather Forecast Messages.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { defineMessages } from 'react-intl';
|
|
6
|
+
|
|
7
|
+
const messages = defineMessages({
|
|
8
|
+
aemetWeatherForecastBlock: {
|
|
9
|
+
id: 'AEMET Weather Forecast',
|
|
10
|
+
defaultMessage: 'AEMET Weather Forecast',
|
|
11
|
+
},
|
|
12
|
+
locationID: {
|
|
13
|
+
id: 'Location ID',
|
|
14
|
+
defaultMessage: 'Location ID',
|
|
15
|
+
},
|
|
16
|
+
locationIDHelp: {
|
|
17
|
+
id: "The Location ID of the AEMET service, for example '41091' to Sevilla location ID.",
|
|
18
|
+
defaultMessage:
|
|
19
|
+
"The Location ID of the AEMET service, for example '41091' to Sevilla location ID.",
|
|
20
|
+
},
|
|
21
|
+
province: {
|
|
22
|
+
id: 'Province',
|
|
23
|
+
defaultMessage: 'Province',
|
|
24
|
+
},
|
|
25
|
+
provinceHelp: {
|
|
26
|
+
id: 'The name of the province of the location (in lowercase) of the AEMET service.',
|
|
27
|
+
defaultMessage:
|
|
28
|
+
'The name of the province of the location (in lowercase) of the AEMET service.',
|
|
29
|
+
},
|
|
30
|
+
defaultFieldset: {
|
|
31
|
+
id: 'Default',
|
|
32
|
+
defaultMessage: 'Default',
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export default messages;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aemet Weather Forecast Schema.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import messages from './messages';
|
|
6
|
+
|
|
7
|
+
export function AemetWeatherForecastSchema({ props, intl }) {
|
|
8
|
+
const schema = {
|
|
9
|
+
title: intl.formatMessage(messages.aemetWeatherForecastBlock),
|
|
10
|
+
block: 'aemetWeatherForecast',
|
|
11
|
+
|
|
12
|
+
fieldsets: [
|
|
13
|
+
{
|
|
14
|
+
id: 'default',
|
|
15
|
+
title: intl.formatMessage(messages.defaultFieldset),
|
|
16
|
+
fields: ['province', 'location_id'],
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
|
|
20
|
+
properties: {
|
|
21
|
+
province: {
|
|
22
|
+
title: intl.formatMessage(messages.province),
|
|
23
|
+
description: intl.formatMessage(messages.provinceHelp),
|
|
24
|
+
widget: 'text',
|
|
25
|
+
},
|
|
26
|
+
location_id: {
|
|
27
|
+
title: intl.formatMessage(messages.locationID),
|
|
28
|
+
description: intl.formatMessage(messages.locationIDHelp),
|
|
29
|
+
widget: 'text',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
required: ['province', 'location_id'],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return schema;
|
|
37
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blocks configuration.
|
|
3
|
+
* @module blocks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ConfigType } from '@plone/registry';
|
|
7
|
+
|
|
8
|
+
// AEMET Weather Current Block
|
|
9
|
+
import AemetWeatherCurrentView from 'volto-aemet/components/Blocks/AemetWeatherCurrent/View';
|
|
10
|
+
|
|
11
|
+
// AEMET Weather Forecast Block
|
|
12
|
+
import AemetWeatherForecastEdit from 'volto-aemet/components/Blocks/AemetWeatherForecast/Edit';
|
|
13
|
+
import AemetWeatherForecastView from 'volto-aemet/components/Blocks/AemetWeatherForecast/View';
|
|
14
|
+
import { AemetWeatherForecastSchema } from 'volto-aemet/components/Blocks/AemetWeatherForecast/schema';
|
|
15
|
+
|
|
16
|
+
// Icons
|
|
17
|
+
import aemetSVG from 'volto-aemet/icons/aemet-logo.svg';
|
|
18
|
+
|
|
19
|
+
export default function install(config: ConfigType) {
|
|
20
|
+
// Add the new block to the group block order
|
|
21
|
+
config.blocks.groupBlocksOrder = [
|
|
22
|
+
...config.blocks.groupBlocksOrder,
|
|
23
|
+
{
|
|
24
|
+
id: 'aemetWeatherForecast',
|
|
25
|
+
title: 'AEMET Weather Forecast',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'aemetWeatherCurrent',
|
|
29
|
+
title: 'AEMET Weather Current',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
// AEMET Weather Current Block Config
|
|
34
|
+
(config.blocks.blocksConfig as any).aemetWeatherCurrent = {
|
|
35
|
+
id: 'aemetWeatherCurrent',
|
|
36
|
+
title: 'AEMET Weather Current',
|
|
37
|
+
group: 'media',
|
|
38
|
+
icon: aemetSVG,
|
|
39
|
+
view: AemetWeatherCurrentView,
|
|
40
|
+
// edit: undefined,
|
|
41
|
+
// blockSchema: undefined,
|
|
42
|
+
restricted: false,
|
|
43
|
+
mostUsed: false,
|
|
44
|
+
sidebarTab: 1,
|
|
45
|
+
security: {
|
|
46
|
+
addPermission: [],
|
|
47
|
+
view: [],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// AEMET Weather Forecast Block Config
|
|
52
|
+
(config.blocks.blocksConfig as any).aemetWeatherForecast = {
|
|
53
|
+
id: 'aemetWeatherForecast',
|
|
54
|
+
title: 'AEMET Weather Forecast',
|
|
55
|
+
group: 'media',
|
|
56
|
+
icon: aemetSVG,
|
|
57
|
+
view: AemetWeatherForecastView,
|
|
58
|
+
edit: AemetWeatherForecastEdit,
|
|
59
|
+
blockSchema: AemetWeatherForecastSchema,
|
|
60
|
+
restricted: false,
|
|
61
|
+
mostUsed: false,
|
|
62
|
+
sidebarTab: 1,
|
|
63
|
+
security: {
|
|
64
|
+
addPermission: [],
|
|
65
|
+
view: [],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return config;
|
|
70
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AEMET Settings.
|
|
3
|
+
* @module settings
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ConfigType } from '@plone/registry';
|
|
7
|
+
import aemetIconSVG from 'volto-aemet/icons/aemet-logo.svg';
|
|
8
|
+
|
|
9
|
+
export default function install(config: ConfigType) {
|
|
10
|
+
// Add the icon for the AEMET settings control panel
|
|
11
|
+
config.settings.controlPanelsIcons = {
|
|
12
|
+
...config.settings.controlPanelsIcons,
|
|
13
|
+
'aemet-settings': aemetIconSVG,
|
|
14
|
+
};
|
|
15
|
+
return config;
|
|
16
|
+
}
|