Differences

This shows you the differences between two versions of the page.

Link to this comparison view

se:labs:07 [2020/03/15 02:04]
emilian.radoi [Suport]
se:labs:07 [2023/10/10 01:14] (current)
emilian.radoi [Feedback]
Line 1: Line 1:
-====== ​Tema Corectare traduceri automate ​======+====== ​Lab 07 Styling & A/B Testing ​======
  
-Desi uneltele de traducere automata (precum Google Translate) s-au imbunatatit semnificativ in ultimii ani, acestea inca prezinta un numar de limitari. Tema aceasta face parte dintr-un proiect care in momentul de fata isi propune construirea unei baze de date cu traduceri de calitate ale unor materiale din domeniul medical pentru a fi folosite drept traduceri de referinta. 
  
-Tema consta in corectarea unor traduceri automate, din limba engleza in limba romana, ale unor articole medicale (scrise pentru publicul larg). 
  
-===== 1. Inregistrare ​=====+===== CSS Styling ​=====
  
-Completati acest **[[https://​forms.gle/​Kkr2RCr6xwK9ceVe6 | FORMULAR]]**  +Next.js has support for native ​**.css** files that can be applied **globally** or at the **component level**.
-  ​Pe baza adresei de email completate, veti primi acces la un director pe Google Drive in care veti avea arhiva “//​temaX.zip//​” si un director gol numit “//​Upload//​”. +
-  ​Arhiva contine 20 articole. Pentru fiecare articol veti avea o traducere obtinuta folosind un tool de traducere automata si varianta originala in limba engleza.+
  
-| {{ :​se:​labs:​temaart1.png?​nolink&​500 |}} |+==== Global Styling ====
  
-===== 2Corectare =====+**pages/​_app.js** is the default app component used by Next.js to initialize each page. It is the starting point of all page components and acts as a blueprint for your pages. Consequently,​ if you want to apply global styling to your Next.js application,​ the stylesheets (.css files) must be included here.
  
-Sunt 2 lucruri importante de urmarit.+<note tip>It is recommended to place the **global ​.css files** in the **styles** directory.</​note>​
  
-=== 2.1. Corectare traducere === +<code css> 
-Parcurgeti in paralel cele 2 variante, si asigurati-va ca varianta in limba romana corespunde cat mai bine celei originale. In cazul in care exista greseli de traducere (nu se transmite bine mesajul, traducerea nu are sens, etc), va trebui sa corectati.+/* styles/​globals.css */
  
-=== 2.2. Adaptare continut (sa fie relevant pentru Romania) === +body { 
-Presupunem ca articolele respective sunt citite de oameni in Romania. +  ​font-family'​Roboto'​'​Helvetica Neue', '​Helvetica',​ sans-serif; 
-  ​* In cazul in care informatia respectiva nu se preteaza pentru cineva din Romania, fiind relevanta pentru cineva care locuieste in UK (de exempluun articol legat de boli de inima prezinta o lista de centre de transplant de inima din UK)atunci va trebui flag-uita partea respectiva (in fisierul //​Readme.txt//​ - vezi mai jos) si cautat ceva echivalent pentru Romania (in cazul in care exista). ​ +  ​padding: 20px 20px 60px; 
-  ​* Continutul trebuie adaptat pentru Romania, sau sters si ‘reparat’ articolul astfel incat sa nu para ca lipseste ceva din el.+  margin: 0 auto; 
 +  background: white; 
 +
 +</​code>​
  
-=== Editarea ​=== +<code javascript>​ 
-Folositi un editor de HTMLPuteti folosi editoare online precum ​[[https://html5-editor.net| html5-editor.NET]].+// pages/​_app.jsx 
 + 
 +import '​../​styles.css'​ 
 + 
 +export default function MyApp({ Component, pageProps }) { 
 +  return <​Component {...pageProps} />; 
 +
 +</​code>​ 
 + 
 +==== Component Styling ==== 
 + 
 +To add component-level CSS, we must use **CSS modules**These are files that use the **[name].module.css** naming convention, can be imported anywhere in the application and help you scope the stylesheet to a particular component. 
 + 
 +<code css> 
 +/* components/​DangerousButton.module.css */ 
 + 
 +.danger { 
 +    color: white; 
 +    background-color:​ red; 
 +
 +.danger:​hover { 
 +    color: red; 
 +    background-color:​ white; 
 +
 +</​code>​ 
 + 
 +For instance, we can use the CSS module to add custom styles to a Material UI button. 
 + 
 +<code javascript>​ 
 +// components/​DangerousButton.jsx 
 + 
 +import styles from '​./​DangerousButton.module.css'​ 
 +import { Button } from "​@mui/​material";​ 
 + 
 +export function DangerousButton(props) { 
 +  return ( 
 +    <Button type="​button"​ className={styles.danger} {...props}>​ 
 +      { props.children } 
 +    </​Button>​ 
 +  ) 
 +
 +</​code>​ 
 + 
 +<note tip> 
 +Note that we are using the **className** property to pass the CSS class to the component. 
 +</​note>​ 
 + 
 +<note tip> 
 +Also, note that we are passing all props further into the Material UI Button component using [[https://developer.mozilla.org/en-US/​docs/​Web/​JavaScript/​Reference/​Operators/​Destructuring_assignment|object restructuring]] (**...props**).  
 + 
 +In this case, **props.children** is a special property that allows us to further pass the content inside the DangerousButton tag into the inner Button tag. 
 +</​note>​
  
 <note important>​ <note important>​
-**Nu schimbati structura tag-urilor!** Doar textul documentului (intre tag-uri). Exceptii pot fi in cazul stergerii unor sectiuni/​informatii daca este nevoie.+All CSS files included in the **_app.jsx** component are applied globally, so make sure the global style directives do not clash with the component-level ones.
 </​note>​ </​note>​
  
 +===== JS Styling =====
  
-<​note>​ +As an alternative to the classic CSS styling, Next.js offers a variety of solutions to **style your React components using JavaScript**:
-**Flag-uiri in //​Readme.txt//​** +
  
-Fiecare flag-uire trebuie sa aiba urmatorul format+  * Inline Styles 
-  - Numar → numar flag-uire (incepand de la 1) +  * [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-styled-jsx|Styled JSX]] 
-  - Titlu → Specificati titlul articolului +  * [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-styled-components|Styled Components]] 
-  - Sectiune veche → Copiati sectiunea cu problema +  ​* [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-emotion|Emotion]] 
-  - Sectiune noua → Copiati sectiunea noua (cea scrisa de voi pentru a rezolva problema+  * [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-linaria|Linaria]] 
-  ​- Observatii ​(Optional→ Descriere ​problemei si a modului in care ati rezolvat-o+  ​* [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-tailwindcss-emotion|Tailwind CSS + Emotion]] 
 +  ​* [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-styletron|Styletron]] 
 +  ​* [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-cxs|Cxs]] 
 +  * [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-aphrodite|Aphrodite]] 
 +  * [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-fela|Fela]] 
 +  * [[https://​github.com/​vercel/​next.js/​tree/​canary/​examples/​with-stitches|Stitches]] 
 + 
 + 
 +While the majority of these solutions are provided through 3rd party libraries, **inline styles** and **styled jsx** are available out of the box. 
 + 
 +==== Inline Styles ==== 
 + 
 +To apply inline styling, we must use the **style** property. 
 + 
 +<code javascript>​ 
 +// components/​DangerousTypography.jsx 
 + 
 +import { Typography } from "​@mui/​material";​ 
 + 
 +export function DangerousTypography(props{ 
 +  ​return ​( 
 +    <​Typography {...props} style={{ color: '​red'​ }}> 
 +      {props.children} 
 +    </​Typography>​ 
 +  ​)
 +
 +</​code>​ 
 + 
 +<note tip> 
 +The value passed to the **style** property is always an **object** (notice the double curly braces) that contains style directives, similar to those written into CSS file.
 </​note>​ </​note>​
  
 +==== Styled Components ====
  
-===== Pasul 3 - Upload ===== +**Styled Components** is one of the most popular libraries for adding style to your React componentsIt can be used globally or at the component levelHoweverwe will only discuss the component-level styling with this library.
-Odata ce ati terminat de corectat articolele:​ +
-  - Impachetati-le intr-o arhiva “//Upload.zip//” si urcati-le in directorul “//​Upload//​” pe Google Drive. +
-  - Separat de arhiva “//​Upload.zip//​”urcati si fisierul “//​Readme.txt//​”. (Acest fisier trebuie sa contina partile flag-uite din articole (in cazul in care acestea exista), si cum le-ati rezolvat. In cazul in care nu ati avut nimic flag-uit, nu este nevoie sa uploadati fisierul “//​Readme.txt//​”.)+
  
-| {{ :se:labs:temaart2.png?nolink&​500 |}} |+Conveniently,​ Material UI natively supports styled components, so we can easily use this library to overwrite the default styling of the MUI components. 
 + 
 +<code javascript>​ 
 +// components/​CoolCheckbox.jsx 
 + 
 +import * as React from '​react';​ 
 +import { styled } from '​@mui/​material/​styles';​ 
 +import Checkbox from "​@mui/​material/​Checkbox";​ 
 + 
 +const StyledCheckbox = styled(Checkbox)` 
 +  color: green!important;​ 
 +`; 
 + 
 +export default function CoolCheckbox({ props }) { 
 +  return ​ <​StyledCheckbox {...props}/>;​ 
 +
 +</​code>​ 
 + 
 +<note tip> 
 +We use **!important** to make sure we override any other inherited styling. You can read [[https://​blogs.halodoc.io/​best-practices-that-we-follow-to-avoid-specificity-issues/#:​~:​text=Below%20are%20the%20order%20of,​These%20selectors%20has%20lowest%20priority.|this article]] if you want to find out more about CSS specificity and directives precedence. 
 +</​note>​ 
 + 
 +===== A/B Testing ===== 
 + 
 +**A/B testing** is an accurate method of testing a particular idea, strategy or feature by performing **user bucketing** and simultaneously delivering **two or more sources of truth** to these buckets. Such solutions are usually integrated with analytics tools, so you can later assess which source of truth renders the best results. 
 + 
 +Let's take a concrete example. You want to display 2 different versions of a button to your users - a blue one and a green one. You want to distribute this experiment evenly among your users, so you would like half of your users to see the green button and the other half the blue one. Moreover, you want this behaviour to be sticky. That means that if a user sees a green button once, they must keep seeing it green until the experiment is over. In the end, we would like to calculate the **conversion rate** of that particular button, so we need to track each click and record the version of the button (green or blue) for such click. Then, we must wait a sufficient amount of time so we have statistically relevant data for our conversion rate comparison. The winning button will be kept and the other one discarded. 
 + 
 +Fortunately,​ there are 3rd party solutions that allow us to perform such an experiment easily. One of them is [[https://​flagsmith.com/​|Flagsmith]] and we chose it for this lab because it offers a **free plan**. 
 + 
 +==== Feature Flags ==== 
 + 
 +Flagsmith allows the creation of **feature flags**, which are the cornerstones of A/B testing. They act as **toggle buttons** that turn various features on and off. 
 + 
 +{{:se:labs:feature-flags-ss.jpg?800}} 
 + 
 +==== A/B Testing ==== 
 + 
 +To use feature flags for **A/B testing**, however, we need to make them **multi-variate**. That means that our feature flags support **variations**,​ and each variation has a particular **value** assigned to it. The **control value** is the default value that gets sent to your application if the user is not yet identified (more on that later). Finally, these values can be weighted so you can decide the percentage of users that should participate in the experiment. 
 + 
 +{{:​se:​labs:​multi-variate-flags.jpg?​700}}
  
 <note important>​ <note important>​
-**Atentie!** +For a classic A/B test experiment, the natural tendency is to use the control value for bucket A. However, because ​**the control value gets sent to the unidentified users no matter the weighting**, the accuracy of the experiment results can be impacted. Consequently, ​in this case, **you should always have two variations besides the control value** - one for bucket A and one for bucket B.
-  * Arhiva trebuie sa fie in format ​**.ZIP**, nu .RAR +
-  * Arhiva .ZIP sa contina DOAR varianta tradusa in limba Romana (NU si originalul)+
 </​note>​ </​note>​
  
 +==== Users ====
  
-===== Notare ​===== +Just like [[https://​ocw.cs.pub.ro/​courses/​se/​labs/​06#​segment|Segment]],​ Flagsmith requires users to be identified. That means that **an explicit id** must be provided for the user that currently uses the application. If we don't explicitly inform Flagsmith about the user's identity, that user will be considered anonymous and a random id will be assigned internally. Even though feature flags continue to work just fine for both identified and unidentified users, **A/B testing can only be performed on identified users**. That means that if an **unidentified user** uses our application,​ they will always see the feature version corresponding to the **control value**. In addition to the control value, the **identified users** can also see the **weighted variations**. 
-**Notarea ​se va face binar**: punctaj maxim pentru o tema bunazero altfel.+ 
 +{{:​se:​labs:​flagsmith-users.jpg?​700|}} 
 + 
 +<note important>​The **unidentified users** will **not** appear in the Users table.</​note>​ 
 + 
 +Users can also have **traits** and they are useful when you want to perform **user segmentation**. For instance, you can choose to perform an A/B test only for males, who have premium subscriptions and are at least 21 years old. 
 + 
 +If you click on a user from the Users table, you can inspect its traits, manually delete them or add new ones, toggle features for that particular user or even change the values of those features. That can be really useful if you have a **development account** that you use to test different functionalities. 
 + 
 +{{:​se:​labs:​flagsmith-user-details.jpg?​700}} 
 +==== Next.js Integration ​==== 
 + 
 +Flagsmith has native support for Next.js, as it can run both on the client and server sides. 
 + 
 +=== Initialization === 
 + 
 +The initialization code must be written in the **pages/​_app.jsx** component on both the client-side and server-side,​ so the flags can be then accessed by all pages. 
 + 
 +<code javascript>​ 
 +// pages/​_app.jsx 
 + 
 +import * as React from '​react';​ 
 +import Head from '​next/​head';​ 
 +import Layout from '​../​layouts/​layout';​ 
 +import '​../​styles/​globals.css';​ 
 +import flagsmith from "​flagsmith/​isomorphic";​ 
 +import { FlagsmithProvider } from "​flagsmith/​react";​ 
 + 
 +const FLAGSMITH_OPTIONS = { 
 +  environmentID:​ '​YOUR_ENVIRONMENT_ID_HERE',​ 
 +  enableAnalytics:​ true, 
 +  preventFetch:​ true 
 +
 + 
 +MyApp.getInitialProps = async () => { 
 +  await flagsmith.init(FLAGSMITH_OPTIONS);​ 
 +  return { flagsmithState:​ flagsmith.getState() } 
 +
 + 
 +export default function MyApp({ Component, pageProps, flagsmithState }) { 
 +  return ( 
 +      <​FlagsmithProvider serverState={flagsmithState} flagsmith={flagsmith} options={FLAGSMITH_OPTIONS}>​ 
 +        <​Head>​ 
 +          <​title>​Lab 7</​title>​ 
 +          <meta name="​description"​ content="​This is the styling and A/B testing lab."/>​ 
 +          <meta name="​viewport"​ content="​initial-scale=1,​ width=device-width"/>​ 
 +        </​Head>​ 
 +        <​Layout>​ 
 +          <​Component {...pageProps} /> 
 +        </​Layout>​ 
 +      </​FlagsmithProvider>​ 
 +  ); 
 +
 +</​code>​ 
 + 
 +<note tip> 
 +You can get your **environment id** from the **Settings** tab of your environment. We recommend using the **Development** environment that Flagsmith provides you with out of the box. 
 +</​note>​ 
 + 
 +{{:se:​labs:​flagsmith-environment.jpg?​900|}} 
 + 
 +=== Feature Flags === 
 + 
 +Feature flags can then be accessed right from the React components. 
 + 
 +<code javascript>​ 
 +// pages/​index.jsx 
 + 
 +import React from '​react';​ 
 +import { Container, Typography } from "​@mui/​material";​ 
 +import { useFlags } from "​flagsmith/​react";​ 
 + 
 +export default function Home() { 
 +  const flags = useFlags(['​google_signup',​ '​payment_message'​]);​ 
 +  const isGoogleSignupEnabled = flags['​google_signup'​]?​.enabled;​ 
 +  const paymentMessageSize = flags['​payment_message'​]?​.value;​ // '​big',​ '​huge'​ or '​control'​ 
 +   
 +  return ( 
 +    <​Container maxWidth="​sm">​ 
 +      <​Typography variant="​p"​ component="​p">​ 
 +        Is Google signup enabled: {isGoogleSignupEnabled} 
 +      </​Typography>​ 
 +      <​Typography variant="​p"​ component="​p">​ 
 +        Payment message size: {paymentMessageSize} 
 +      </​Typography>​ 
 +    </​Container>​ 
 +  ); 
 +
 +</​code>​ 
 + 
 +<note tip> 
 +Even though Flagsmith allows you to fetch the flags on the backend (in **getStaticProps** and **getServerSideProps** functions), you might not be able to rely on the values of these flags because users usually get identified on the client side. Of course, this can be solved with cookies, but that exceeds the scope of this lab. 
 +</​note>​ 
 + 
 +=== Users === 
 + 
 +Because we don't have a login system integrated into our app, we will identify a fictional user right after the app initialization code. 
 + 
 +<note tip> 
 +If we want to test the behaviour of our app for **other users**, we will simply **change the user id** (traits can stay the same) in the code and refresh the page. 
 +</​note>​ 
 + 
 +<code javascript>​ 
 +// pages/​_app.jsx 
 + 
 +MyApp.getInitialProps = async () => { 
 +  await flagsmith.init(FLAGSMITH_OPTIONS);​ 
 +  await flagsmith.identify( 
 +    '​user_90619',​ 
 +    { 
 +      fullName'Marie Curie', 
 +      email: 'marie.curie@gmail.com',​ 
 +      age: 23, 
 +      gender: '​female',​ 
 +      isPremiumUser:​ false 
 +    } 
 +  ); 
 +  return { flagsmithState:​ flagsmith.getState() } 
 +
 +</​code>​ 
 + 
 +<note tip> 
 +In all the other components that call the **useFlags** function, Flagsmith will return the flags assigned to the user identified using the code above. The values of these flags will depend on which bucket (A or B) the user belongs to. The **bucketing** is performed by Flagsmith automatically. 
 +</​note>​ 
 +===== Tasks ===== 
 + 
 +<note tip> 
 +Again, we will use **Material UI** as a component library for our project. All the necessary dependencies have already been included, so you can make use of the readily available components. You can check out the [[https://​mui.com/​material-ui/​|official docs]] to understand how can you include those components in your code. Some tasks will ask you to style these components to add an extra layer of customization. 
 + 
 +The app uses the [[https://​documenter.getpostman.com/​view/​1946054/​S11HvKSz#​1e9464c5-6bef-421e-8f07-1a3523a2d98a|Cat Facts API]] and the GET requests are performed using [[https://​stackoverflow.com/​questions/​68150039/​use-axios-in-getstaticprops-in-next-js|Axios]]. 
 + 
 +The necessary dependencies for the **Styled Components** and **Flagsmith** are also included in the project. 
 +</​note>​
  
-  * Se asteapta un nivel foarte ridicat al calitatii. In cazul in care se gasesc 1-2 greseli mari / evidente in cadrul unei teme (per tema, nu per articol), tema respectiva va primi zero puncte. 
-  * Sunteti sfatuiti sa le verificati cu atentie inainte de uploadare pentru a nu lasa greseli sa scape. 
  
 <note important>​ <note important>​
-**Urmatoarele lucruri trebuie respectate pentru ca tema sa fie luata in considerare:​**   +This time, we are using **yarn** as a package manager. So make sure you [[https://classic.yarnpkg.com/​lang/​en/​docs/​install/#​mac-stable|install it]] before tackling the tasksIf you want to know more about it, you can read a comparison between yarn and npm [[https://​www.imaginarycloud.com/​blog/​npm-vs-yarn-which-is-better/​|here]].
-  * Respectati formatul de uploadarhiva in format ​.ZIP care sa contina doar articolele in limba romana corectate. +
-  * Nu schimbati structura tag-urilor HTML. +
-  * Folositi diacritice.+
 </​note>​ </​note>​
  
-===== Suport =====+  - Download the {{:​se:​labs:​se-lab7.zip|project archive}} for this lab and run ''​yarn install''​ and ''​npm run dev''​. 
 +  - Create a blue button using **Styled Components** in ''​components/​styled/​BlueButton.jsx''​. 
 +  - Create a green button using **CSS modules** in ''​components/​vanilla/​GreenButton.jsx''​. Add the corresponding CSS module in the same directory. 
 +  - Make the welcome message of the ''​pages/​index.jsx''​ page component **bold** using **inline styling**. 
 +  - Integrate the global styling written in the ''​styles/​globals.css''​ file into the app and remove the horizontal and top padding. 
 +  - Create a free [[https://​flagsmith.com/​|Flagsmith]] account, set up a fictive organisation and a project called **Lab 7**. 
 +  - Create a feature in the **Development** environment called **button_color**. Create **two variations**. Give a default value to the control state and suggestive values to the variations (these values should describe the button colours). The **weighting** should be 50-50 (the control value should have weight 0). 
 +  - Write your environment id and place the user identification code in ''​pages/​_app.jsx''​. 
 +  - Conditionally display the **See Tasks** button in ''​pages/​index.jsx''​. If the **button_color** feature flag is disabled, it should be a ''​DangerousButton''​. Conversely, the button should be a ''​GreenButton''​ or ''​BlueButton''​ depending on the feature flag value. 
 +  - Observe how the button changes when you start identifying new users. Check the Users table in the Flagsmith dashboard as well.
  
-Pentru intrebari sau nelamuriri legate tema folositi acest formular: ​**[[ https://​forms.gle/L7SdPM1B2P1W7PuEA | FORMULAR_NELAMURIRI ]]**+<note tip>You should lookout for the **TODO**s in the code.</note>
  
 +====== Feedback ======
  
 +Please take a minute to fill in the **[[https://​forms.gle/​PNZYNNZFrVChLjag8 | feedback form]]** for this lab.
  
se/labs/07.1584230657.txt.gz · Last modified: 2020/03/15 02:04 by emilian.radoi
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0