feat: add challenge 49

This commit is contained in:
Timothy Alcaide
2024-04-11 17:41:27 +02:00
committed by thomas
parent 6b148a243a
commit 928ec929f8
26 changed files with 357 additions and 16 deletions

View File

@@ -24,7 +24,7 @@ If you would like to propose a challenge, this project is open source, so feel f
## Challenges
Check [all 48 challenges](https://angular-challenges.vercel.app/)
Check [all 49 challenges](https://angular-challenges.vercel.app/)
## Contributors ✨

View File

@@ -0,0 +1,36 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts"],
"extends": [
"plugin:@nx/angular",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
]
}
},
{
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
}
]
}

View File

@@ -0,0 +1,13 @@
# Hold to send button
> author: thomas-laforge
### Run Application
```bash
npx nx serve rxjs-hold-to-save-btn
```
### Documentation and Instruction
Challenge documentation is [here](https://angular-challenges.vercel.app/challenges/rxjs/49-hold-to-save-btn/).

View File

@@ -0,0 +1,22 @@
/* eslint-disable */
export default {
displayName: 'rxjs-hold-to-save-btn',
preset: '../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/apps/rxjs/hold-to-save-btn',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};

View File

@@ -0,0 +1,79 @@
{
"name": "rxjs-hold-to-save-btn",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/rxjs/hold-to-save-btn/src",
"tags": [],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/rxjs/hold-to-save-btn",
"index": "apps/rxjs/hold-to-save-btn/src/index.html",
"browser": "apps/rxjs/hold-to-save-btn/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/rxjs/hold-to-save-btn/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"apps/rxjs/hold-to-save-btn/src/favicon.ico",
"apps/rxjs/hold-to-save-btn/src/assets"
],
"styles": ["apps/rxjs/hold-to-save-btn/src/styles.scss"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "rxjs-hold-to-save-btn:build:production"
},
"development": {
"buildTarget": "rxjs-hold-to-save-btn:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "rxjs-hold-to-save-btn:build"
}
},
"lint": {
"executor": "@nx/eslint:lint"
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/rxjs/hold-to-save-btn/jest.config.ts"
}
}
}
}

View File

@@ -0,0 +1,26 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
standalone: true,
imports: [],
selector: 'app-root',
template: `
<main class="flex h-screen items-center justify-center">
<div
class="flex w-full max-w-screen-sm flex-col items-center gap-y-8 p-4">
<button
class="rounded bg-indigo-600 px-4 py-2 font-bold text-white transition-colors ease-in-out hover:bg-indigo-700">
Hold me
</button>
<progress [value]="20" [max]="100"></progress>
</div>
</main>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {
onSend() {
console.log('Save it!');
}
}

View File

@@ -0,0 +1,5 @@
import { ApplicationConfig } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [],
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>rxjs-hold-to-save-btn</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<app-root></app-root>
</body>
</html>

View File

@@ -0,0 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';
bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err),
);

View File

@@ -0,0 +1,14 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
progress {
@apply h-4 w-full rounded-full bg-gray-200;
}
progress::-webkit-progress-bar {
@apply rounded-full bg-gray-200;
}
progress::-webkit-progress-value {
@apply rounded-full bg-indigo-600;
}

View File

@@ -0,0 +1,2 @@
import '@testing-library/jest-dom';
import 'jest-preset-angular/setup-jest';

View File

@@ -0,0 +1,14 @@
const { createGlobPatternsForDependencies } = require('@nx/angular/tailwind');
const { join } = require('path');
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
join(__dirname, 'src/**/!(*.stories|*.spec).{ts,html}'),
...createGlobPatternsForDependencies(__dirname),
],
theme: {
extend: {},
},
plugins: [],
};

View File

@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": []
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
}

View File

@@ -0,0 +1,6 @@
{
"extends": "./tsconfig.json",
"include": ["src/**/*.ts"],
"compilerOptions": {},
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
}

View File

@@ -0,0 +1,33 @@
{
"compilerOptions": {
"target": "es2022",
"useDefineForClassFields": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.editor.json"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"extends": "../../../tsconfig.base.json",
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}

View File

@@ -0,0 +1,15 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node", "@testing-library/jest-dom"]
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@@ -1,5 +1,5 @@
{
"total": 48,
"total": 49,
"🟢": 18,
"🟠": 121,
"🔴": 209

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -0,0 +1,46 @@
---
title: 🟠 Hold to save button
description: You're tasked with implementing Lucie's button design, requiring holding it for a set time to save, taking over from Sacha; functionalities include configuring duration, countdown initiation on "mousedown", progress bar reset on "mouseleave" or "mouseup", reflecting remaining time, and simulating save request on hold completion, using RxJS operators and ensuring declarative code.
author: timothy-alcaide
contributors:
- alcaidio
challengeNumber: 49
command: rxjs-hold-to-save-btn
sidebar:
order: 19
badge: New
---
## Context
As a member of the development team, you have to respond to a specific request from the UX designer, 👩🏻‍🎨 Lucie, who has designed a button that must be held down for X amount of time to save a save request.
Sacha 👶🏼 the trainee has already integrated the design but doesn't know how to perform the "holdable" functionality.
So you're going to take over from him.
## Functional expectation
> "As a user, I would like to save something by holding down the button for a certain amount of time."
Here the prototype made by Lucie :
![prototype gif](../../../../../../docs/src/assets/rxjs/49/prototype.gif)
## Acceptance Criteria
1. We should be able to configure a maintenance duration in milliseconds.
2. Pressing and holding the button triggers the countdown on "mousedown" event.
3. On "mouseleave" or "mouseup" events, the progress bar is reset to 0.
4. The progress bar representing the remaining relative time should reflect the remaining time.
5. Simulates a backup request when the hold time is over (console log or alert).
6. You must maximize the use of RxJS operators and be as declarative as possible.
<details>
<summary>Tips 🤫 (if you really need it and after careful consideration)</summary>
<ul>
<li>Create the `HolddableDirective`</li>
<li>Use `TemplateRef` and `fromEvent` from rxjs to catch events OR `@HostListener`</li>
<li>Perhaps the following rxjs operators can help you: interval, takeUntil, switchmap, takeWhile/retry...</li>
</ul>
</details>

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow
variant: primary
- text: Ir al Desafío más reciente
link: /es/challenges/forms/48-avoid-losing-form-data/
link: /es/challenges/rxjs/49-hold-to-save-btn/
icon: rocket
- text: Dar una estrella
link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid>
<Card title="48 Desafíos">
Este repositorio contiene 48 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>.
<Card title="49 Desafíos">
Este repositorio contiene 49 Desafíos relacionados con <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> y <b>Typescript</b>.
Estos desafíos se resuelven en torno a problemas de la vida real o características específicas para mejorar tus habilidades.
</Card>

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow
variant: primary
- text: Aller au dernier Challenge
link: /fr/challenges/forms/48-avoid-losing-form-data/
link: /fr/challenges/rxjs/49-hold-to-save-btn/
icon: rocket
- text: Donne une étoile
link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid>
<Card title="48 Défis">
Ce répertoire rassemble 48 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences.
<Card title="49 Défis">
Ce répertoire rassemble 49 Défis liés à <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> et <b>Typescript</b>. Ces défis portent sur des problèmes réels ou des fonctionnalités spécifiques pour améliorer vos compétences.
</Card>
<Card title="Subscribe to get notify of latest challenges">

View File

@@ -27,8 +27,8 @@ import MyIcon from '../../components/MyIcon.astro';
import SubscriptionForm from '../../components/SubscriptionForm.astro';
<CardGrid>
<Card title="48 Challenges">
This repository gathers 48 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
<Card title="49 Challenges">
This repository gathers 49 Challenges related to <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> and <b>Typescript</b>.
These challenges resolve around real-life issues or specific features to elevate your skills.
</Card>

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow
variant: primary
- text: Ir para o desafio mais recente
link: /pt/challenges/forms/48-avoid-losing-form-data/
link: /pt/challenges/rxjs/49-hold-to-save-btn/
icon: rocket
- text: Dar uma estrela
link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid>
<Card title="48 Desafios">
Este repositório possui 48 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>,
<Card title="49 Desafios">
Este repositório possui 49 Desafios relacionados a <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>,
<b>Ngrx</b> e <b>Typescript</b>.
Esses desafios são voltados para problemas reais ou funcionalidades específicas afim de
melhorar suas habilidades.

View File

@@ -13,7 +13,7 @@ hero:
icon: right-arrow
variant: primary
- text: Перейти к последней задаче
link: /ru/challenges/forms/48-avoid-losing-form-data/
link: /ru/challenges/rxjs/49-hold-to-save-btn/
icon: rocket
- text: Добавить звезду
link: https://github.com/tomalaforge/angular-challenges
@@ -26,8 +26,8 @@ import MyIcon from '../../../components/MyIcon.astro';
import SubscriptionForm from '../../../components/SubscriptionForm.astro';
<CardGrid>
<Card title="47 испытаний">
Этот репозиторий содержит 47 испытаний, связанных с <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> и <b>Typescript</b>.
<Card title="49 испытаний">
Этот репозиторий содержит 49 испытаний, связанных с <b>Angular</b>, <b>Nx</b>, <b>RxJS</b>, <b>Ngrx</b> и <b>Typescript</b>.
Испытания основаны на реальных задачах или инструментах для того, чтобы прокачать вас.
</Card>